|
Previous articles in this series:
- ASP.NET MVC Dev Series I - Using the Membership and Role Providers
- ASP.NET MVC Dev Series II - Revisiting authentication (avoid using the <location/> element in Web.config)
If you've been reading Scott Guthrie's blog then you probably already know how to pass data from a controller to a view. There are two ways that you can do this. The first and most flexible means is by using the ViewData object as a dictionary. The following few lines of code illustrate what it looks like to pass data from a controller to a view using the loosely-typed ViewData dictionary:
[ControllerAction]
public void Index()
{
...
MyData[] dataElements = (from MyData data in myContext.Source select data).ToArray();
ViewData["MyData"] = dataElements;
RenderView("Index");
}
In this method you see that we're using some magic strings as dictionary keys to pass to the view data dictionary. I generally try and avoid this scenario entirely and I use a facade pattern like this:
[ControllerAction]
{
....
MyData[] dataElements = (from MyData data in myContext.Source select data).ToArray();
MyDataFacade facade = new MyDataFacade();
facade.DataElements = dataElements;
RenderView("Index", facade);
}
This is all well and good, but what if, in Index.aspx, you want to use a user control? Let's say that this control is responsible for rendering a list of banking ledger transactions. You can easily imagine how your app might need to do this rendering in many different places but with the same or similar GUI - a perfect use for a control. How do we get that control to render the data we want it to render without tightly coupling it and without using weak-typed ViewData dictionaries? Well, there's actually some magic in the ViewData property that isn't immediately obvious that makes this possible.
First, you want to include your user control on the Index.aspx view:
<Custom:LedgerItemsList id="ledgerItems" runat="server" ViewDataKey="LedgerItems" />
Make note of the ViewDataKey property. Ordinarily, we would use this to tell the control which dictionary key would be used in a loosely typed dictionary to fetch data. Check out the code we can write below (in LedgerItemsList.ascx):
<%
LedgerItem[] ledgerItems = (LedgerItem[])(Page as ViewPage).ViewData[ViewDataKey];
foreach .... %>
It looks like we're using ViewData as if it were a dictionary, but we know that we have a strongly-typed view - how will this stuff even compile??? The trick is that if you use a strongly typed view (e.g. ViewPage<MyFacade> instead of ViewPage) the ViewData property indexer method actually works differently than normal, utilizing Reflection to find a property on the strongly-typed data object that matches the dictionary key you supplied. So in this case, I passed in a value of "LedgerItems" for the ViewDataKey. Instead of searching a dictionary for a key of "LedgerItems", it used reflection and found a property named LedgerItems on my facade object!
Too. Damn. Cool.
Not bad. I'm a believer in user controls. I have not ventured into strong
use with mvc. I will be doing so soon. I'll post when I get some good
examples. Thanks for the lead in.