Home

Controllers

Basics

Every page requested in a Vici MVC web application is first passed to a method of a controller. This is a method which takes the request, performs some action, and renders the "view" (usually HTML).

A controller class should be declared as a class inheriting from Controller:

public class HomePage : Controller
{
   public void Run()
   {
   }
}

Note the public void Run() method. This is the action method that will be executed by default. It is possible to have other methods called, but we'll get to that later on.

By default, a controller will render the view with the same name as the class. In the example above, the view "HomePage" will be rendered inside the default "master" layout. Views and layouts are discussed in the Views section but for now, just remember that a layout is a skeleton HTML file in which the view HTML is rendered.

The controller is responsable for providing data to the view. The way this is done is by adding variables to the ViewData collection, which is a property of the Controller class. The ViewData collection is a dictionary where you can store any data type, from simple integers to complete collections of complex bussiness objects. The Vici MVC template language used for the view can retrieve this data and render it to the web browser.

public class HomePage : Controller
{
   public void Run()
   {
      ViewData["Greeting"] = "Hello World!";
      
      ViewData["Users"] = DataLayer.GetUserList();
   }
}

Specifiying views

There are two ways you can specify which view to render:

  • Add a [View(...)] attribute to the controller class or action method
  • Call the RenderView(...) method

The [View("ViewName")] attribute tells the controller or action method which view to render. The view name should not include the ".htm" extension. You can specify a full relative path as the view name. If you don't, Vici MVC will search for the HTML file in the root template folder.

[View("Public/Home")] // This will render the view "<template folder>/Public/Home.htm
public class HomePage : Controller
{
   public void Run()
   {
      ViewData["Greeting"] = "Hello World!";
   }
}

The second way of specifying a view is by calling the RenderView(...) method with the view name as the only parameter. This allows you to change the rendered view at runtime:

public class HomePage : Controller
{
   public void Run()
   {
      ViewData["Greeting"] = "Hello World!";
      
      RenderView("Public/Home"); // This will render the view "<template folder>/Public/Home.htm
   }
}

Calling RenderView() does not render the view immediately. It just tells the framework which view to render when the controller method has finished executing. This also implies that you can call RenderView() more than once. Only the last call will have effect.

Changing the layout is possible by specifying the [Layout(...)] attribute or by calling the ChangeLayout(...) method.

Similar to RenderView(), calling ChangeLayout() does not render the view immediately. It just tells the framework what layout to use.

Useful methods

The base Controller class defines a few useful methods you can use:

IsPost()

Signature: bool IsPost();

Returns true if the request method is "POST".

This method is also available in the static WebAppContext class.

IsPost(controlName)

Signature: bool IsPost(string controlName);

Returns true if the request method is "POST" and the post data contains a variable named controlName which is not empty. This is very usefull to check if a specific submit button has been pressed. In HTML, when you click a submit button, an item will be added to the post data with the name of the button, and the label of the button as its value.

This method is also available in the static WebAppContext class.

Redirect(newUrl)

Signature: void Redirect(string newUrl);

Is actually an alias for Response.Redirect(). It will redirect the browser immediately to a new URL. The URL can be relative or absolute (starting with "~/")

Mapping controllers to URLs

Controllers are mapped to specific URLs by the URL router. The router's routing table determines which controller to create and which action method to call. By default, the mapping table is:

/{controller}/{action}/{id} Maps to the {action} method of the {controller} class, passing {id} as a parameter
/{controller}/{action}      Maps to the {action} method of the {controller} class
/{controller}               Maps to the Run() method of the {controller} class

You can map a specific URL to a controller by specifying the [Url(...)] attribute on the controller class or on the action method:

[Url("Public/HomePage")]
public class HomePage : Controller
{
   public void Run()
   {
      ViewData["Greeting"] = "Hello World!";
      
      RenderView("Public/Home"); // This will render the view "<template folder>/Public/Home.htm
   }
}

Note that the URL should be a relative path (relative to the web site root path)

If you use the [Url] attribute to map a URL to a controller class or action method, you are actually adding entries to the main URL routing table. The order in which these routes are added is undefined, so be careful to choose unique URL mappings.

This only scratches the surface of the URL mapping engine. For example, you can embed parameters in URLs. For a more thorough explanation of the routing engine, check out the section on URL routing.

BeforeAction/AfterAction methods

It is possible to define methods in your controller class that will be executed before and after the action method (the default action method is Run(), but can be anything you like, depending on the URL routing configuration).

Every method decorated with the attribute [BeforeAction] will be executed before the action method. Methods which have the [AfterAction] attribute applied will be executed after the action method. BeforeAction and AfterAction methods can be private, but it's better to make them public if your web application needs to run in a partial trust environment.

These before/after actions are especially useful in custom base classes. In the following example we created 2 separate controller base classes. One for use in public pages, and one for pages which require a user to be authenticated.

public class PublicPageController : Controller
{
   [BeforeAction]
   private void SetLayout()
   {
       ChangeLayout("PublicLayout");
   }
}

public class AuthenticatedPageController : Controller
{
   [AfterAction]
   private void Authenticate()
   {
       if (!(bool)Session["Authenticated"])
          Redirect("~/login.ashx");
   }
}

Another use for [BeforeAction] methods is the inclusion of Javascript code in the view. You can call RegisterJavascript() in the [BeforeAction] method to include some javascript in your rendered view. For more information on rendering Javascript, check out the section on Javascript Rendering

It's important to keep in mind that the order in which these before/after methods are executed is undefined. It is only guaranteed that base class methods are called before methods in derived classes.

Creating controllers at runtime

Vici MVC allows you to create controllers in your action methods (or from any other code for that matter). This is how you create a controller object:

public void SomeActionMethod()
{
    using (MySubController subController = CreateController<MySubController>(View))
    {
        subController.AnotherActionMethod(100);
    }
}

A few things need explaining here:

The CreateController() method expects one parameter: the view object to use. Usually this is is the view from the calling controller, but there are scenarios where you want to create a new view object and pass that one to the subcontroller.

Another important fact to know is that the Controller class implements IDisposable (hence the use of "using" in the example). The Dispose() method takes care of calling the [AfterAction] methods. If you don't have [AfterAction] methods in your controller, you can safely skip calling Dispose(), but it's good practice to always call it anyway.

[BeforeAction] methods will be called when the controller object is created. Because the controller is not created by the URL routing engine, automatic parameter mapping will not be performed for any of the controller methods. If you do declare parameters in your [BeforeAction/AfterAction] methods, they will be uninitialized. Keep that in mind.