MVC doesn't necessarily have "events" in the web-forms sense of the word (though you can use them) but it is generally not advised. MVC corresponds to the HTTP model for sending / receiving data from a server. When you want to interact with the server, you need additional action methods on your controller classes.
Your controller can accept input from the client in several ways but the most basic of these is by specifying arguments to your action methods which MVC will attempt to bind to the request data (form, querystring, etc). Here is an example controller:
//Controller class
public class ReportController : Controller {
public ActionResult Index() {
ActionResult result = null;
ReportSelectionViewModel viewModel = this.BuildViewModel();
result = View("Index", viewModel);
return result;
} // end action Index
[HttpPost()]
public ActionResult ChangeReport(ChangeReportRequest changeRequest) {
ActionResult result = null;
ReportSelectionViewModel viewModel = this.BuildViewModel();
Session["ReportOptions"] = changeRequest;
viewModel.Messages.Add("Selection was changed.");
viewModel.ReportRequest = changeRequest;
result = View("Index", viewModel);
return result;
} // end action ChangeReport
//Method that encapsulates the logic needed to build a view model for actions on this controller
protected ReportSelectionViewModel BuildViewModel() {
ReportSelectionViewModel viewModel = null;
viewModel = new ReportSelectionViewModel();
viewModel.AvailableThresholds.AddRange(new int[] { 1, 2, 3, 4 });
//Set the value to whatever the user previously selected if available
if (Session["ReportOptions"] != null) {
viewModel.ReportRequest.ThresholdSelect = ((ChangeReportRequest)Session["ReportOptions"]).ThresholdSelect;
} // end if
return viewModel;
} // end function BuildViewModel
} // end class ReportController
In the above controller, the default Index()
action does not accept any arguments because it does not require any for the first visit. The ChangeReport()
action accepts an argument of type ChangeReportRequest
called a model. This tells MVC that this action is EXPECTING a value (or series of values) to come from the client and causes MVC to dig deeper to find out which values are supplied by the client to fulfill the controller's argument parameter and return a fully populated model object.
Here is what that the model class looks like:
//Model class that gets built by the DefaultModelBinder
public class ChangeReportRequest {
private int _reportId;
private int _thresholdSelect;
public ChangeReportRequest() {
_reportId = 1; // Meaningful default value here
_thresholdSelect = 0;
} // end constructor
public int ReportId {
get {
return _reportId;
} set {
_reportId = value;
}
} // end property ReportId
public int ThresholdSelect {
get {
return _thresholdSelect;
} set {
_thresholdSelect = value;
}
} // end property ThresholdSelect
} // end class ChangeReportRequest
It is also a good idea to have a different type of model (a ViewModel
) that is used specifically for display purposes (which differs from a normal model whose purpose is to hold transactional data). ViewModels add compile-time support in your view for which things can be displayed in the view. An example ViewModel
is shown below:
//Strongly typed view model class passed to the view to add intellisense support in the view and avoid the ViewBag.
using System.Collections.Generic;
public class ReportSelectionViewModel {
private List<int> _availableThresholds;
private List<string> _messages;
private ChangeReportRequest _reportRequest;
public ReportSelectionViewModel() {
_availableThresholds = new List<int>();
_messages = new List<string>();
_reportRequest = new ChangeReportRequest();
} // end constructor
public List<int> AvailableThresholds {
get {
return _availableThresholds;
}
} // end property AvailableThresholds
public List<string> Messages {
get {
return _messages;
}
} // end property Messages
public ChangeReportRequest ReportRequest {
get {
return _reportRequest;
} set {
if (value != null) {
_reportRequest = value;
} // end if
}
} // end property ReportRequest
} // end class ReportSelectionViewModel
MVC has built-in model binding that will try to map form fields to input values on controller action methods. By naming the controller method parameter the same as your select list (you must give your select list a name in addition to it's ID), you can simply create a a form wrapper around your select list:
Here is the view that would be used with the above code:
@model ReportSelectionViewModel
<h2>Index</h2>
<div class="message-wrapper">
<ul class="messages">
@foreach(string message in Model.Messages) {
<li>@message</li>
}
</ul>
</div>
<form method="post" action="@Url.Action("ChangeReport")">
<select id="ThresholdSelect" name="ThresholdSelect">
@foreach(int item in Model.AvailableThresholds) {
<option value="@item" @(Model.ReportRequest.ThresholdSelect == item ? "selected" : "")>@item</option>
}
</select>
<!--Later this can be made into a text box or drop down or whatever, as long as the name matches the model-->
<input type="hidden" name="ReportId" value="@Model.ReportRequest.ReportId" />
<input id="submit" type="submit" name="submit" value="Select" />
</form>
The default MVC ModelBinder is smart. It uses reflection via the ModelMetaDataProvider
to iterate over the parameters in your method and attempt to find fields from the HTTP request that match based on their name (which is why it's important to have the form fields named). It can do things more complex that an int or a string as well.
Here are some resources: