Controls and Basic Helpers Examples

HTML 5 Input of type Range NEW

 

<%:Html.RangeFor(m => m.Percentages[index], new { style = "text-align:right", min=0d, max=100d })%>

 

 

DateTimeInput and DateRange attribute

The DateTimeInput control renders either a DateTime or a DateTime?  property with separate DropDowns for Year, Month, Day, Hour, Minute, and Seconds. Year is rendered with a ComboBox only if the control is able to compute a finite range for its possible values with the help of all DateRange attributes (described below) applied to the relative DateTime property. In any case one can force the use of a TextBox by setting the useTextBoxForYear  parameter to true. This control needs reference to both MVCControlToolkit.Controls.js and jquery-1.4.1.js.

Date and Time can be rendered in separate places of the View and they are localized either according to a Culture furnished as input or according to the current UI culture. One can also choose to render just one of them. However, if you render only the Time, please select the  useDateHidden option. This way the original Date part of the DateTime will be recovered when the view is posted, otherwise the .Net default date will be used (the Date part of default(DateTime)) that might be different from your default Date choice.

The DateTimeInput  control limits the possible choices in the DropDowns only to the values compatible with the Range constraints applied to the relative DataTime property. Constraints are specified with the DateRange attribute that allows also dynamic Minimum and  dynamic Maximum be defined using other properties. Constraints are applied also on the client side, thus changes to one of a couple of related properties on the View will be immediately reflected on the other property on the client side . 

Below a description of the DateRange attribute.

One can apply more than a DataRange attribute to each DateTime property. Each of them can specify both  static Minimum and Maximum and dynamic Minimum and Maximum. The  property C  to be used as either dynamic maximum or as dynamic Minimum for a property X  is defined with a string containing the dot separated list of properties to reach C from the father object of X(the object where the property X is defined). The value of C will be used both to do server side validation and to force the possible choices of the DropDowns of X on the client side. If C is rendered in the View, the values inserted by the user for C  will be used to constrain the possible input for X directly on the client side. 

Delays can be applied to both dynamic minima and dynamic maxima in the DateRange attribute. For instance, one can requires X be 5 days before C. What Happens on client side when a constraint is violated depends on the value of the RangeType property of the DateRange attribute:

 

  1. X has RangeType set to Verify and C is not rendered as an input field and is decorated with the MileStone attribute. The value of X is forced to conform with the constraint imposed by C.
  2. X has RangeType set to Verify and C is  rendered as an input field . The value of X is forced to conform to the constraint imposed by the value of C. When the user changes the value of C, the change is immediately reflected both on the value of X and on the possible choices in the DropDowns of X, specifically: the value of X will be possibly changed to satisfy the constraint imposed by C, and the DropDowns of X will only allow values compatible with such a constraint.
  3. X has RangeType set to Propagate and C is  rendered as an input field and has a  simmetric DateRange attribute with a TargetType set to Propagate .  When the user changes the value of one of them the value of the other changes atuomatically to conform with the constraint. For instance, suppose you have an activity whose minimum duration is one week. By using a RangeType of Propagate for both the start and the end date of the activity and a delay of one week, the user can change both dates freely but when it do a change to one of them the other date is changed to enforce the one week minimum duration constraint.

The MileStone attribute declares that a property has been inserted in the Vie Model just to drive the user to insert an input confroming with some constraints, and that its value will not be rendered as an input fileld.

Delays can be specified either as static TimeSpans or dynamically, however in any case changes in their values on the client side are not supported(it makes no sense to allow the user to change the delay).

It is worth to point out that each property may have several DateRange attributes. Thus, complex network of constraints can be handled on the client side(be careful to avoid circular dependencies and inconsistent constraints).

Caution need to be taken when verifying dynamic constraints on the server side, because the sensible data defining the constraint (that maybe either a dynamic delay or a dynamic minimum or maximum) cannot be taken from the model extracted by the data posted back by the user. Data coming from the client side are always to be considered unreliable because of  both malicious users or simple errors. Therefore validation need to be performed or repeated after having retrived these sensible data from a data store.

Below an example of use for the situation depicted in the list item 2.  You can change it easily to experiment also the other situations.

Property declaration:

 

 	[DateRange(DynamicMaximum = "Stop", RangeAction = RangeAction.Verify, 
            DynamicMaximumDelay = "Delay", SMaximum = "2012-1-1", SMinimum = "2008-1-1")]
        public DateTime Start {get;set;}

        [MileStone]
        public TimeSpan Delay{get;set;}

        [DateRange(SMaximum = "2012-1-1", SMinimum = "2008-1-1")]
        public DateTime Stop { get; set; }

 

Rendering of the Controls:

 

              <div>
                <%var DTS = Html.DateTimeFor(m => m.Start, DateTime.Now);  %>
                <%: DTS.Date() %>&nbsp;&nbsp;<%: DTS.Time() %>
                </div>
              <div><br /><br /></div>
                <div>
                <%var DT = Html.DateTimeFor(m => m.Stop, DateTime.Now);  %>
                <%: DT.Date() %>&nbsp;&nbsp;<%: DT.Time() %>
              </div>

 

 

The second parameter of the DateTimeFor helper specifies the default date to display when the initial value of the property is null.

Other optional parameters allows also the specification of Html attributes and of a Culture to use for the date (otherwise the current UI culture is used).

CheckBox list for selecting elements

The CheckBoxList control allows items in a list to be chosen by selecting CheckBoxes instead of using a Multiselect DropDown or ListView.  

Suppose the View Model has a prpoperty Roles,  containing the codes of all roles assigned to a user. The Role property can be any IEnumerable with an Add method.  Moreovere, suppose for sack of simplicity, that a list of all roles is contained in the static IEnumerable property,  MVCNestedModels.Models.RegisterModel.AllRoles whose items contains a Code and a Name property with respectively the role code and the role name.

In order to let the user select the roles with the CheckListBox control you need as a first step to create a ChoiceList data structure from the Role IEnumerable with the instruction:

new ChoiceList<RoleInBlog, int, string>( RegisterModel.AllRoles,(t => t.Code), (t => t.Name))

The two delegates (t  => t.Code) and (t  => t.Code) select the fields to be used repectively as value field and as display field.

Then you can pass the ChoiceList to the CheckBoxListFor Helper in your View, yielding:

<%: Html.CheckBoxListFor(m => m.Roles,
       new ChoiceList<RoleInBlog, int, string>(
              RegisterModel.AllRoles, (t => t.Code), (t => t.Name))
%>

or:

<%: Html.CheckBoxListFor(m => m.Roles,
                         new ChoiceList<RoleInBlog, int, string>(RegisterModel.AllRoles,(t => t.Code),(t => t.Name)),
                         true, "RoleListTemplate" )  
%>

The first code snippet displays the CheckBox list with a standard template while the second one displays the CheckBox list with the custom template "RoleListTemplate". The ChoiceList constructor has also an overload with two more aeguments.These two extra arguments are two delegates that, when specified, are called  on each CheckBoxListItem to get respectively the CheckBox  and the CheckBox label Html attributes. This way, each item can have a look that depends on the dat it contains.

The CheckBoxListFor Helper not only displays the CheckBox list but also takes care of invoking an adequate IDisplayModel handler to recompose the original Roles list once the View is posted.

DualSelect Box

With a dual select box items of a collection can be selected by transferring them from a ListBox to another through a series of buttons. There are buttons for :

  • Selecting one or more items
  • De-selecting one or more items
  • Selecting all Items
  • De-selecting all items
  • Changing the order of the selected items

The two ListBoxes and the 6 buttons to handle the items can be placed separately whrever the user want. Moreover,  it is possible to specify Html attributes for each of them. This control needs reference to  MVCControlToolkit.Controls.js.

Below an example of use(it uses the Choice list as the CheckBox list):

 

                  <% var
                         RoleSelect = Html.DualSelectFor(m => m.Roles,
                            new ChoiceList<RoleInBlog, int, string>(
                                RegisterModel.AllRoles,
                                (t => t.Code),
                                (t => t.Name)));
          
                  %>
                  <table>
                  <tr>
                      <td colspan="3" valign="middle" align="center">
                      <%:RoleSelect.ClearSelectionButton("Unselect All") %>
                      <%:RoleSelect.UnSelectButton("Move Left") %>
                      <%:RoleSelect.SelectButton("Move Right") %>
                      <%:RoleSelect.SelectAllButton("select All Roles") %>
                   </td>
                  </tr>
                  <tr>
                       <td  valign="middle" align="center">
                         Available Roles
                       </td>
                       <td  valign="middle" align="center">
                        Selected Roles
                       </td>
                       <td  valign="middle" align="center">
                        
                       </td>
                  </tr>
                  <tr>
                       <td  valign="middle" align="center">
                        <%:RoleSelect.AvailableItems(
                            new Dictionary<string, object>
                            {
                                {"style", "height:150px; width: 100px;"}
                            }
                         ) %>
                       </td>
                       <td  valign="middle" align="center">
                       <%:RoleSelect.SelectedItems(
                            new Dictionary<string, object>
                            {
                                {"style", "height:150px; width: 100px;"}
                            }
                         ) %>
                       </td>
                       <td  valign="middle" align="center">
                        <div><%:RoleSelect.GoUpButton("Move Up") %></div>
                        <div><%:RoleSelect.GoDownButton("Move Down") %></div>
                       </td>
                  </tr>
                  </table>

 

 

Editing a list in a TextBox

Suppose now you want to enter a list of keywords as a comma separated list of words in a single textbox, and then you want to collect all keywords in a IEnumerable property of the View Model of name Keywords:

<%: Html.PackedListFor(m => m.Keywords, ",") %>

Also this time not only the helper method converts the IEnumerable into a comma separated list of words, but also it invoke the right handler to recompose back the list in the original format once the View is posted. Other overloads of the helper allow the keyword be rendered into a hidden field, to apply Html attributes and also to use a custom template. 

This functionality is the base for a series of javascript based controls that exchange data with the server through hidden fields containing items separated by a separator. One might object that JSon do a better job...Yes, Of Course! However, we don't want a View Model exposes any JSon data but only data that it is natural for it to expose. We don't want the View Model depends on How one has chosen to display the data in the View but only on What data to display, so View Model properties containing JSon objects are not an option! JSon is a good option for controllers methods working as web services to provide data for Ajax calls...but this is another another story.

Editing a list with delete and add options

Thanks to the support offered by the MVCControlToolkit for the IUpdateDisplay interface  one can enrich the Edit Views of Lists with support for addition and deletion of elements without writing  any code in the controller. Suppose we want to edit the Keywords of the previous example keeping them in separate TextBoxes. We need the possibility to edit and delete each keyword, and the possibility of adding a new keyword. 

The following code snippet renders the already  existing elements of the list with a delete-item operation support:

<%: Html.RenderEnumerableFor(m => m.Keywords, "Keyword", true, ItemType.AutoDelete)  %>
  1. The first argument selects the property, as usual,
  2. The second argument is the name of a strong typed template to be used to render each item, 
  3. The third argument specifies if support for additions and deletions is required
  4. The ItemType enumeration specifies how deletions are handled
    • ItemType => AutoDelete: Items are considered deleted when they are removed from the DOM. In case of string fields deletion is caused by empty fields, in case of simple types deletion is caused by default value, otherwise some simple javascript is needed to remove the items from the DOM. 
    • ItemType => Simple: Items are never removed. If a data item is removed from the DOM a null (or defaut in case of value type) value is inserted into the List.
    • ItemType => HandlesDelete: Each item is embedded into a wrapper with two properties: Item and Deleted(bool initially set to false). In this case, typically, the Delete property is rendered into a hidden input. To delete the element some javascript code sets this hidden input to true.  If a data item is removed from the DOM without setting Deleted  a null (or defaut in case of value type) value is inserted into the List.

The following code snippet add also a TextBox for inserting a new keyword:

<%: Html.RenderAddItemFor<RegisterModel, List<string>, string>(m => m.Keywords, "Keyword", ItemType.AutoDelete) %>

Arguments of the helper are the same, but The ItemType requires some explanation:

  • ItemType => AutoDelete: In case of string fields, if the imput field is not empty a new item is created. In case of simple types, if the imput field is different from the default a new item is created. In case of complex types if the item is not removed from the DOM a new tem is created.
  • ItemType => Simple: a new item is always created.
  • ItemType => HandlesDeleteEach item is embedded into a wrapper with two properties: Item and Deleted(bool initially set to true). In order to create the new element some javascript code needs to set to false the Deleted field that is initially set to true;

That's all! You don't need  any further code! When a TextBox is empty the relative item is deleted from the list, while it is enough to write a new keyword in the last TextBox for inserting it in the list. All changes are made automatically when the View is posted!

Update/Insert/Delete/ Templated Datagrid NEW

My choice for implementing a general purpose DataGrid is to embed the Data into a wapper Tracker<T>  containing a bool Changed and two properties of the same type of the Data: Value and OldValue.

  1. When the Item is deleted Value is set to null but OldValue contains the old data to be deleted in the database. The change is signalled by setting Changed to true.
  2. When the Item is created Value is set to the newly created while OldValue is set to null. The change is signalled by setting Changed to true.
  3. When the item is modified the Value and  OldValue contain the two versions of the data item and the change is signalled by setting Changed to true. 

The introduction of the wrapper donesn't break the separation of concerns between the View Model and the View because the wrapper isn't needed because of a particular choice about How to render the data, but on the contrary it is needed to support the controller in reflecting the data changes in the database.

The DataGrid keeps The OldValue between consecutive posts. When the user finally decide to move the changes to the DB he can call the Confirm() method of the Tracker<T> that sets the OldValue to the same value of the Value and resets the Change bool. Calling Cancel() cancels the modifications by setting the Value equal to the OldValue, and resetting the Change bool.

This control needs reference to both MVCControlToolkit.Controls.js and jquery-1.4.1.js.

The user can edit sevral rows, delelete several rows and insert a new row before posting.

User can customize the DataGrid by providing :

  • A Partial View to be used as overall container for the whole grid
  • The type of container to be used for the items: div, span, tr, td, ....etc(HTML5 tags are also supported ). The type of Item container can be also decided dynamically by providing a function that returns it based on the current item and on the current item position
  • A Partial View to be used as Dsiplay View Item Template
  • A Partial View to be used as Insert/Edit View Item Template
  • A Partial View to be used for the new row insertion item, when in Display mode(in Edit mode it uses the normal edit template)
  • A function that returns the separator to be used between items as a function of the current item, and of its position.

Operations on the DataGrid are done with the help of customizable DataButtons that can be of type: Insert, Edit, Delete and Cancel

Validation Helpers showing validation errors for each item can be inserted on both the edit and display templates.

Below an example.

The overall Template for the Grid:

 

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCNestedModels.Models.ToDoItem>" %>

<table>
<tr>
<td colspan="4"> TO DO LIST</td>
</tr>
<tr>
<td><strong>Name</strong></td>
<td><strong>Description</strong></td>
<td>Edid</td>
<td>Delete</td>
</tr>
<%:ViewData["Content"] as MvcHtmlString %>
</table>

 

 

the ViewData["Content"] renders all grid items.

The Display Template:

 

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCNestedModels.Models.ToDoItem>" %>
<%@ Import Namespace=" MVCControlsToolkit.Core" %>
<%@ Import Namespace=" MVCControlsToolkit.Controls" %>

            <td class="editor-field">
                <%: Html.ValidationMessageFor(m => m.Name, "*")%><%: Model.Name %>
            </td>
            <td class="editor-field">
                <%: Html.ValidationMessageFor(m => m.Description, "*")%><%: Model.Description %>
            </td>
            <td class="editor-label">
                <%: Html.DataButton(DataButtonType.Edit, "Edit Item", null) %>
            </td>
            <td class="editor-label">
                <%: Html.DataButton(DataButtonType.Delete, "Delete Item", null) %>
            </td>

 

 

Please note the usage of the DataButtons.

The Edit Template:

 

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MVCNestedModels.Models.ToDoItem>" %>
<%@ Import Namespace=" MVCControlsToolkit.Core" %>
<%@ Import Namespace=" MVCControlsToolkit.Controls" %>
            
            <td class="editor-field">
                <%: Html.ValidationMessageFor(m => m.Name, "*")%><%: Html.TextBoxFor(m => m.Name) %>
            </td>
            <td class="editor-field">
                <%: Html.ValidationMessageFor(m => m.Description, "*")%><%: Html.TextBoxFor(m => m.Description) %>
            </td>
            <td class="editor-label" colspan="2">
                <%: Html.HiddenFor(m => m.Code) %>
                <%: Html.DataButton(DataButtonType.Cancel, "Cancel Changes", null) %>
            </td>

 

Please note the usage of the DataButtons

The insert item template:

 

<%@ Import Namespace=" MVCControlsToolkit.Core" %>
<%@ Import Namespace=" MVCControlsToolkit.Controls" %>

<td colspan="4"><%: Html.DataButton(DataButtonType.Insert, "Insert New To Do Item", null)%></td>

 

Finally, the instruction to insert the Grid in your View:

 

 <div>
                
     <%: Html.DataGridFor(m => m.ToDoList, ItemContainerType.tr, "ToDoEditItem", "ToDoDisplayItem", "ToDoGrid", "ToDoInsertItem") %>

 </div>

 

There are parameters also for specifying the attributes of the edit and display containers, the separators and possibly to specify also the item container dynamically.

 

IEnumerable Conversion and Transformations chaining

The RenderEnumerableFor helper is able to operate only on List<T>,  but the ConvertToList helper is able to convert any IEnumerable that either is an Array or that conforms to the following conditions:

  • It has an Add or AddItem method
  • It has a constructor with no parameters or with a a single initial lenght parameter.

When the page is posted back the IEnumerable is converted back to its initial type.  Suppose now that the keywords in the previous example are stored into an Array, the code becomes:

 

<% RenderInfo<List<string>> convertedKeywords =
                        Html.ConvertToList<RegisterModel, string[], string>(m => m.Keywords); %>
 <%:  
   Html.RenderEnumerableFor(convertedKeywords, "Keyword", true, ItemType.AutoDelete)    
%>
                  
<%: 
   Html.RenderAddItemFor<RegisterModel, List<string>, string>(convertedKeywords, "Keyword", ItemType.AutoDelete)    
%>

The RenderInfo<T> class is the standard format used to chain transformation operations in the MVCControlToolkit. Most of MVCControlToolkit helpers accept both Lambda Expressions or RenderInfo<T> to specify the data they have to operate on.  The RenderInfo<T> format enables them to accept as input the output of other helpers.

Often, when taking the input from the output of a previous transformation, one needs to extract just a part of the output. In such cases one can use the Extract helper:

 

public static  RenderInfo<NM> Extract<VM, OM, NM>(
            this HtmlHelper<VM> htmlHelper,
            Expression<Func<OM, NM>> expression,
            RenderInfo<OM> originalModelInfo)

The output from the previous transformation is passed to the originalModelInfo parameter, while the sub-part to extract is specified by the Lambda Expression expression.

 

Core Function Examples

Designing Controls with the IDispalyModel interface

Designing new Controls is as easy as implementing the IDisplayModel Interface, plus writing a template and possibly some javascript.

Suppose we want to display a Date contained in a DateTime property as 3 different integer inputs. Later,  the separated Year, Month and Day can be rendered with DropDowns or TextBoxes. The problem here is recomposing back Year, Month and Day in the original DateTime when the View is posted. You can say to the MVCControlsToolkit how to translate the two different representations one into the other simply by implementing the  IDisplayModel Interface:

 public class TestDateTimeDisplay : IDisplayModel
{
 
        [Range(1000, 3000, ErrorMessage = "wrong year")]
        
        public Nullable<int> Year { get; set; }

        [Range(1, 12, ErrorMessage = "wrong month")]
        public Nullable<int> Month { get; set; }

        [Range(1, 31, ErrorMessage = "wrong day")]
        public Nullable<int> Day { get; set; }

        public object ExportToModel(Type targetType, params object[] context)
        {
            if (Year == 0 && Month == 0 && Day == 0) return null;
            if (!Year.HasValue && !Month.HasValue && !Day.HasValue) return null;
            try
            {
                return new DateTime(Year.Value, Month.Value, Day.Value);
            }
            catch (Exception ex)
            {
                throw (new Exception(" {0} has an incorrect date format", ex));
                
            }
        }

        public void ImportFromModel(object model, params object[] context)
        {
            Nullable<DateTime> date = model as Nullable<DateTime>;
            if(date.HasValue && date.Value != DateTime.MinValue)
            {
                Year = date.Value.Year;
                Month = date.Value.Month;
                Day = date.Value.Day;
            }

        }
    }

Exceptions thrown in the ExportToModel method are automatically transformed into error messages that are associated with the initial DateTime property and that can be Displayed with the ValidationMessageFor helper. The initial DateTime property can be decorated with annotations that will be used when displaying the View.

The context parameter of the ImportFromModel method is passed by the helper that displays the control and can be used do drive how the control is diplayed. Pay attention: The same parameter will not be automatically recovered  at posting time. Thus, if you need it when the page is posted you have to put it in some field of your class.

The context parameter of the ExportToModel method at the moment are not used and are reserved for future expansions.

A IDisplayModel interface implementation defines a reversible transformation that is applied on the View Model. The inverse transformation is automatically applied by the MVCControlToolkit default binder when the View is posted back. 

Once defined your reversible transformation you can invoke it with one of the two overloads of InvokeDisplay helper:

 

public static RenderInfo<NM> InvokeTransform<VM, M, NM>(
            this HtmlHelper<VM> htmlHelper,
            Expression<Func<VM, M>> expression,
            NM newModel,
            object[] args = null)
            where NM : IDisplayModel


public static RenderInfo<NM> InvokeTransform<VM, M, NM>(
            this HtmlHelper<VM> htmlHelper,
            RenderInfo<M> renderInfo,
            NM newModel,
            object[] args = null)
            where NM : IDisplayModel

 

 

The output of the invocation is a RenderInfo class that can be used as input to another transformation. this way transformations can be chained. The second overload is used when the input comes from a previous transformation. The optional args parameter is passed as context to the ImportFromModel method. 

The simplest way to render the output of the invocation is through the RenderIn helper:

 

public static MvcHtmlString RenderIn<VM, M>(
            this HtmlHelper<VM> htmlHelper,
            string template,
            RenderInfo<M> renderiNFO)

 

Where the template parameter specifies the name of a strongly typed template for displaying your control. Thus, the code for displaying our simple date ontrol is:

 

<%: Html.RenderIn("YearMonthDayDate", Html.InvokeTransform(m => m.PersonalData.BirthDate, new TestDateTimeDisplay()))  %>

 

That's all! 

You can also decide to write an Html helper. In such a case you need to call the InvokeTransform method from your helper. The RenderInfo<T> that is returned contains:

  1. The model to render, 
  2. The prefix to use, 
  3. A string to chain with  your final output string. You can get this string by calling the GetPartialRendering method. Pay Attention! This call returns a non-empty string only at its first invocation! This behaviour is a protection to avoid this string be rendered more than once.

Designing Controls with the IUpdateModel interface

The IUpdateModel Interface allows one to combine data from different properties at any depth in the View Model(or in a subpart of the View Model) into a new data structure to be rendered. When the View is posted back the input received into this new data structure is used to update the initial model in a custom way . The same properties of the View Model can be used by several  implementations of the IUpdateModel interface and can be update several times when the View is posted. The peculiarities of the IUpdateModel interface are used in the implementation of the RenderEnumerableFor helper.

Suppose we want to implement a control to display various  quantities of type float in the View Model as percentages plus their sum. This transformation allows the use of Sliders to allocate the a total quantity to different destinations. A simple example of use is the partition of a total investment into a portfolio of separated investments.

 

public class PercentageUpdater:IUpdateModel
    {
        public float[] Percentages { get; set; }
        public string[] Labels { get; set; }
        public float Total { get; set; }
        public object UpdateModel(object model, string[] fields)
        {
            float partialTotal = 0f;
            foreach (float percentage in Percentages) partialTotal += percentage;
            if (partialTotal != 0f)
            {
                for (int index = 0; index < fields.Length; index++)
                {
                    new PropertyAccessor(model, fields[index]).Value = (Percentages[index]/partialTotal) * Total;
                }
            }
            return model;
        }

        public void ImportFromModel(object model, object[] fields, string[] fieldNames, object[] args = null)
        {
            Percentages=new float[fields.Length];
            Labels=new string[fieldNames.Length];
            Total = 0;
            for(int index = 0; index < fields.Length; index++ )
            {
                Percentages[index] = (float)fields[index];
                Labels[index]=new PropertyAccessor(model, fieldNames[index]).DisplayName;
                Total += Percentages[index];
            }
            if (Total != 0f)
            {
                for (int index = 0; index < fields.Length; index++)
                {
                    Percentages[index] = Percentages[index]*100 / Total;
                }
            }
        }
    }

 

As in the case of the IDisplayModel, IUpdateModel contains the methods form performing both the initial transformation and the transformation to apply when the page is posted. In our case when the page is posted, pecrcentages are transformed back into absolute values.

The first parameter of the UpdateModel method contains the initial model, while the second parameter contains all the properties to be used for creating the new data structure in string format (a string of names separated by dots). The PropertyAccessor class allows access to the properties located by such strings. In particolar we can set, get the value of the property and we can retrive its attributes, and in particular the value of the DisplayName attribute that is used to define the label to apply when the property is rendered. Our Control uses the DisplayName to render the label of each percentage.

The first parameter of the ImportFromModel method contains the model, the second one the values of the fields to be used and the third one their names (as before names separated by dots). The last parameter is a set of optional arguments passed by the method that invokes the implementation of the ImportFromModel interface.

Once defined the IUpdateModel one can invoke the transformation it defines with the help of the InvokeUpdateTransform helper:

 

public static RenderInfo<NM> InvokeUpdateTransform<VM, M, NM>(
            this HtmlHelper<VM> htmlHelper,
            Expression<Func<VM, M>> expression,
            NM newModel,
            string[] expressions=null,
            object[] pars=null)
            where NM : IUpdateModel

public static RenderInfo<NM> InvokeUpdateTransform<VM, M, NM>(
            this HtmlHelper<VM> htmlHelper,
            RenderInfo<M> renderInfo,
            NM newModel,
            string[] expressions = null,
            object[] pars=null)
            where NM : IUpdateModel

 

Rendering is done as in the case of the IDisplayModel interface. Below an example of use in an investment application:

 

<%:Html.RenderIn("Investments",
                    Html.InvokeUpdateTransform(m => m,
                        new PercentageUpdater(),
                        new string[]
                        {
                            "PersonalData.ForexInvestment",
                            "PersonalData.FuturesInvestment",
                            "PersonalData.SharesInvestment"
                        }))
                 %>

 

That's all!

Look for more examples and tutorials on my Blog 

Last edited Oct 12, 2010 at 8:28 AM by frankabbruzzese, version 56

Comments

No comments yet.