Apr 24 2011

Designing a Themed Control in the Mvc Controls Toolkit

Category: MVCFrancesco @ 07:09

Mvc Controls Toolkit Datagrid Updated Tutorial (more recent version of Defining MVC Controls 2: Using the DataGrid)

Advanced Data Filtering Techniques in the Mvc Controls Toolkit

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

All controls of the Mvc Controls Toolkit are designed to allow the maximum flexibility. For instance, the DataGrid is not table based but allows a generic template to be used as item template. Other controls,such as the DateTimeInput, and the DualSelectBox are composed of several parts that can be rendered separately. Features like, Sorting, Paging and Filtering are not included in controls like the DataGrid and the SortableList but are provided through separate helpers. Finally, all controls can be styled with Css. While this approach ensures an high level of flexibility, the time needed to set-up completely a control is higher compared to the time needed to setup a pre-configured control.

In order to promote re-usability of efforts made to set up a control, the Mvc Controls Toolkit allow the definition of Themes. Each Theme is composed of a Css enriched with an image folder, and of a set of Partial Views. Each Partial View features an already set-up control. While the layout and all parts each control is composed of are defined in the Partial Views, the Css define completely the style of all controls. Thus, the controls of each theme are ready to be used, with no further effort; we just need to pass data to them! Better configuration and style options are offered by the commercial version of the Mvc Controls Toolkit.

In this post I will show how to design a control to be inserted into a theme. I assume a basic knowledge of the Theming features of the Mvc Controls Toolkit. Therefore, readers are encouraged to read the documentation page about theming here before reading this post.

We will call “Test” the theme we are going to define, and we will analyze just how to design a Table based Themed Datagrid offering paging, sorting and column filtering services.,

As a first step we set-up our folders as shown below:

css_folder_treefolder_tree

Now let open the DataGrid.cshtml file. The Themed Datagrid is invoked by the helper:

  1. public static MvcHtmlString ThemedDataGridFor<M, TItem>(
  2.             this HtmlHelper<M> htmlHelper,
  3.             Expression<Func<M, List<Tracker<TItem>>>> expression,
  4.             GridFeatures gridFeatures,
  5.             Columns<TItem> fields,
  6.             Expression<Func<M, List<KeyValuePair<LambdaExpression, OrderType>>>> orderExpression = null,
  7.             Expression<Func<M, int>> page=null,
  8.             Expression<Func<M, int>> prevPage=null,
  9.             Expression<Func<M, int>> pageCount=null,
  10.             Expression<Func<M, Expression<Func<TItem, bool>>>> filter=null,
  11.             string title=null,
  12.             string name = "DataGrid"
  13.             )
  14.             where TItem:class, new()

expression defines the collection to be rendered in the Grid, filter will collect the filtering criteria (see here), orderExpression the sorting criteria(see here), and page, prevPage, and pageCount the paging (see here). gridFeatures is a bit flag enumeration that defines the desired features selected by the user of the Themed DataGrid:

[Flags]
    public enum GridFeatures {None = 0, Edit=1, Display=2, Insert=4, Delete=8, ResetRow=16, UndoEdit=32, InsertOne=64, Paging=128, Sorting=256, Filtering=512}

For more information see the Documentation page about theming.

While fields is a collection of column definitions. Each column contains the column features(if sorting, and filtering are allowed, and an optional column header to override the one provided through DataAnnotations), and possibly Edit and Display templates for the column. One can also define custom columns that are not connected with specific fields.For more information see the Documentation page about theming.

The ThemedDataGridFor helper just packs all information received into a GridDescription object defined as:

public class GridDescriptionBase
    {
        public GridFeatures Features { get; set; }
        public List<Column> Fields { get; set; }   
    }
    
    public class GridDescription:GridDescriptionBase
    {
        public dynamic ToShow {get; set;}
        public dynamic ToOrder { get; set; }
        public string Title { get; set; }
        public dynamic HtmlHelper { get; set; }
        public dynamic Page { get; set; }
        public dynamic PrevPage { get; set; }
        public dynamic PageCount { get; set; }
        public dynamic Filter { get; set; }

    }

Then it passes this object to our DataGrid.cshtml Partial View into: ViewData["ThemeParams"]

Thus our first step is to “unpack” this object:

@{
    var options = ViewData["ThemeParams"] as GridDescription;

    Func<dynamic, HelperResult> editTemplate = null;
    if ((options.Features & GridFeatures.Edit) == GridFeatures.Edit)
    {
        editTemplate = EditItem;
    }
    Func<dynamic, HelperResult> displayTemplate = DisplayItem;
    Func<dynamic, HelperResult> insertTemplate = null;
    if ((options.Features & GridFeatures.Insert) == GridFeatures.Insert)
    {
        insertTemplate = InsertItem;
    }
}

The above instruction just verifies if the Insert and Edit services are required, and if they are required assign the name of the Insert template(InsertItem), and of the Edit template(EditItem) to the variables inserItem, and editItem. The Display template, that is not optional, is always put into the displayItem variable.

Templates are defined through Razor helpers(see the documentation about templates) that use the columns information provided by the GridDescription class to render each single item.

Below the Insert template that just renders an insert button:

@helper InsertItem(dynamic htmlHelper)
    {
        var options = htmlHelper.ViewData["ThemeParams"] as GridDescription;
    int count = 0;
    bool hasCustomField = false;
    foreach (Column column in options.Fields)
    {
        if ((column.Features & FieldFeatures.Hidden) != FieldFeatures.Hidden)
        {
            count++;
        }
        else if (column.DispalyTemplate != null)
        {
            hasCustomField = true;
        }
    }
    if ((options.Features & GridFeatures.Edit) == GridFeatures.Edit
        || hasCustomField
        || (options.Features & GridFeatures.Delete) == GridFeatures.Delete) { count++; }
    <td colspan='@count' class="Theme-DataGrid-Column">
        @DataGridHelpers.ImgDataButton(
            htmlHelper, DataButtonType.Insert,
            Url.Content("~/Content/themes/test/images/add.jpg"), null)</td>
}

 

 

 

Let’s forget for just a minute about the hasCustomField variable, we will discuss about it when analyzing the Display template.The foreach loop just counts the number of columnsto be inserted in the colspan to format properly the table.If the grid is required to work also in Edit mode or if it is required to allow row deletes we adds one column more for the some command buttons. Columns marked as hidden are jumped since they contains just hidden field that will not be rendered together with the other columns.

The imgDataButton helper that renders the insert button takes an image form the Css that is part of the “Test” theme. Its is rendered in a quite strange way, namely,  it is not invoked as an extension method but just as a normal static method of the DataGridHelpers class. This is because we are using dynamic  variables(see the GridDescription definition) to handle Types that will be known just a run-time since our DataGrid is required to work with any class. In such a case extension methods cannot be used!

In the Display template we do some initialization and then we start a foreach loop on all columns:

@helper DisplayItem(dynamic htmlHelper)
    {
        var options = htmlHelper.ViewData["ThemeParams"] as GridDescription;
        bool hasCustomField=false;
        foreach (Column column in options.Fields)

We will see what the hasCustomField variable is needed for in a short time, let’s see first how “normal” columns(the ones that are not marked as Hidden) are dealt with:

if ((column.Features & FieldFeatures.Hidden) != FieldFeatures.Hidden)
            {
            <td class="Theme-DataGrid-Column">
            @{
                if (column.DispalyTemplate == null)
                {
                      @DisplayExtensions.DisplayFor(htmlHelper, column.Field)
                }
                else
                {
                      @CoreHTMLHelpers.TemplateFor(htmlHelper, column.DispalyTemplate)
                }
             }
            </td>
            }

 

Very simple: if the user has provided a Display template for that column we just invoke it through the TemplateFor helper otherwise we simply invoke the standard Mvc DisplayFor helper. It is worth to point out that column templates are passed information on the whole row, since we passed the htmlHelper for the whole row to the TemplateFor helper. This way, each column can use information about other columns in the rendering. Moreover, we may define “custom” columns that are associated to no specific field by by using  m => m as filed selector.

Now let’s see what happens to fields marked as Hidden:

else if(column.DispalyTemplate != null){
                hasCustomField = true;
            }

If they have a custom display template we just set the hasCustomField variable, defined at the beginning of the helper to true, just to remember we have found one of them. We have done the same thing in the Insert template. Why?? Simple! Because hidden fields are rendered in the edit Templates, since they just store data for a future post, thus a display template associated with an hidden field for sure is needed to render a command button. Probably a command button that uses the principal key of the row to render either a detail view of the record or some other children objects by means of either an action link or an Ajax call. Therefore, in such a case we need to display a further column reserved for all command buttons:

if ((options.Features & GridFeatures.Edit) == GridFeatures.Edit
            || hasCustomField
            || (options.Features & GridFeatures.Delete) == GridFeatures.Delete)
        {
        <td>
            <table>
                <tr>
                    @if ((options.Features & GridFeatures.Edit) == GridFeatures.Edit)
                    {         
                        <td>
                            @DataGridHelpers.ImgDataButton(
                                htmlHelper, DataButtonType.Edit,
                                Url.Content("~/Content/themes/test/images/edit.jpg"), null)
                        </td>
                    }
                    @if ((options.Features & GridFeatures.Delete) == GridFeatures.Delete)
                    {
                        <td>
                            @DataGridHelpers.ImgDataButton(
                                htmlHelper, DataButtonType.Delete,
                                Url.Content("~/Content/themes/test/images/delete.jpg"), null)
                        </td>
                    }
                    @if (hasCustomField)
                    {
                        foreach (Column column in options.Fields)
                        {
                            if ((column.Features & FieldFeatures.Hidden) == FieldFeatures.Hidden &&
                                column.DispalyTemplate != null)
                            {
                                <td>
                                    @CoreHTMLHelpers.TemplateFor(htmlHelper, column.DispalyTemplate)
                                </td>
                            }
                        }
                    }
               </tr>
            </table>
        </td>
        }

The Edit template is completely analogous, just the command buttons are different:

@helper EditItem(dynamic htmlHelper)
    {
        var options = htmlHelper.ViewData["ThemeParams"] as GridDescription;

        foreach (Column column in options.Fields)
        {
            if ((column.Features & FieldFeatures.Hidden) != FieldFeatures.Hidden)
            {
            <td class="Theme-DataGrid-Column">
            @{
                if (column.EditTemplate == null)
                {
                      @EditorExtensions.EditorFor(htmlHelper, column.Field)
                }
                else
                {
                    if (column.EditTemplate is string)
                    {
                        string templateName = column.EditTemplate as string;
                        templateName = "Themes/Test/" + templateName;
                        @CoreHTMLHelpers.TemplateFor(htmlHelper, templateName)
                    }
                    else
                    {     
                        @CoreHTMLHelpers.TemplateFor(htmlHelper, column.EditTemplate)
                    }
                }
             }
            </td>
            }
        }
        <td>
            <table>
                <tr>          
                    <td>
                        @DataGridHelpers.ImgDataButton(
                            htmlHelper,
                            DataButtonType.Cancel,
                            Url.Content("~/Content/themes/test/images/undo.jpg"), null)
                    </td>
                    @if ((options.Features & GridFeatures.UndoEdit) == GridFeatures.UndoEdit)
                    {
                        <td>
                            @DataGridHelpers.LinkDataButton(
                                htmlHelper,
                                DataButtonType.ResetRow,
                                ThemedControlsStrings.Get("Item_ResetRow", "DataGrid"), null)
                        </td>
                    }
                    @foreach (Column column in options.Fields)
                    {
                        if ((column.Features & FieldFeatures.Hidden) == FieldFeatures.Hidden)
                        {
                            if (column.EditTemplate != null)
                            {
                                @CoreHTMLHelpers.TemplateFor(htmlHelper, column.EditTemplate)
                            }
                            else
                            {
                                <td style="width:0px">
                                    @InputExtensions.HiddenFor(htmlHelper, column.Field)
                                </td>
                            }
                        }
                    }
               </tr>
            </table>
        </td>
}

It is convenient to define also a Razor helper that renders the Header of the table, with all column names, sort, and filtering logics:

@helper DisplayHeader(dynamic htmlHelper, GridDescription options)
    {
    <tr>
        @{bool hasCustomField=false;}
        @foreach (Column column in options.Fields)
        {
            if ((column.Features & FieldFeatures.Hidden) != FieldFeatures.Hidden)
            {
            <td class="Theme-DataGrid-Header">
                <table>
                <tr>

The above Razor Helper is completely analogous to the other templates. In each column we have 2 sections. The first one is dedicated to filtering and the second one is dedicated either to a sort button with the column name inside it or just to the column name if the column is not sortable.

Let’s start with the filtering:

@if ((options.Features & GridFeatures.Filtering) == GridFeatures.Filtering && (column.Features & FieldFeatures.Filtering) == FieldFeatures.Filtering)
                {
                    <td >
                    <div class="Theme-DataGrid-Filter">
                        <div class="MvcControlsToolkit-Hover">
                            @{
                            var h1 = DataFilterClauseHelpers.DataFilterClauseFor(
                                htmlHelper,
                                options.Filter,
                                column.Field,
                                "first");
                            var h2 = DataFilterClauseHelpers.DataFilterClauseFor(
                                htmlHelper,
                                options.Filter,
                                column.Field,
                                "second");
                            bool oldClientSetting = ViewContext.ClientValidationEnabled;
                            ViewContext.ClientValidationEnabled = false;
                                }
                              <table>
                                <tr>
                                    <td>
                                        @InputExtensions.CheckBox(h1, "Selected", h1.ViewData.Model.Selected)
                                    </td>
                                    <td>
                                        @DataFilterClauseHelpers.FilterClauseSelect(h1, h1.ViewData.Model.Condition, column.Field)
                                    </td>   
                                </tr>
                                <tr>
                                    <td colspan="2">
                                        @{object val1=h1.ViewData.Model.Search;
                                          if (! h1.ViewData.Model.Selected)
                                          {
                                              val1 = string.Empty;
                                          }
                                        }
                                        @InputExtensions.TextBox(h1, "Search", val1)
                                        @{ValidationExtensions.Validate(h1, "Search"); }
                                    </td>
                                </tr>
                              </table>
                              <table>
                                <tr>
                                    <td>
                                        @InputExtensions.CheckBox(h2, "Selected", h2.ViewData.Model.Selected)
                                    </td>
                                    <td>
                                        @DataFilterClauseHelpers.FilterClauseSelect(h2, h2.ViewData.Model.Condition, column.Field)
                                    </td>   
                                </tr>
                                <tr>
                                    <td colspan="2">
                                        @{object val2=h2.ViewData.Model.Search;
                                          if (!h2.ViewData.Model.Selected)
                                          {
                                              val2 = string.Empty;
                                          }
                                         }
                                        @InputExtensions.TextBox(h2, "Search", val2)
                                        @{ValidationExtensions.Validate(h2, "Search"); }
                                        @{ViewContext.ClientValidationEnabled=oldClientSetting;}
                                    </td>
                                </tr>
                              </table>    
                            </div>
                        </div>
                    </td>
                    
                }

Filters are implemented through an hover menu that appears on mouse hover a filtering icon. All hover menu logic is contained in the Css, so we don’t have to worry about this while coding the filtering logics. Each column may contains two filter clauses, this is the minimum to implement min-max conditions. Accordingly we create the two helpers h1 and h2 to renders the filters by means of the DataFilterClauseFor helper.

We use each helper to render a checkbox to enable the filter criterion, a dropdown to select the filtering condition, and finally a TextBox for inserting the Filter parameter and the associated validation helper. We ensure that if the filter is disabled the TextBox is empty.

What’s beautiful about the DataFilterClauseFor helper is that once we have Rendered input fields, for its “Search” field, for its filter criterion(our DropDown), and for its enabled condition…it handles all filtering logics for us….We will receive the complete filter obtained combining all columns filters in our the filter property of our ViewModel..So we need just to apply it to an IQueryable to filter it (see also here for more information).

Let’s move to the second section (sorting and column names):

<td>
                <strong>
                @if ((column.Features & FieldFeatures.Sort) == FieldFeatures.Sort)
                {
                    @DataGridHelpers.SortButtonForTrackedCollection(
                        htmlHelper,
                        options.ToShow,
                        column.Field,
                        sortButtonStyle: SortButtonStyle.Button)
                }
                else
                {
                    if (column.ColumnHeader != null)
                    {
                        @column.ColumnHeader
                    }
                    else
                    {
                       @DataGridHelpers.ColumnNameForTrackedCollection(
                       htmlHelper,
                       options.ToShow,
                       column.Field)
                    }
                }
            </strong>
            </td>

options.Toshow contains the lambda expression that defines the collection to be shown in the grid. If the column sorting is required we render a sort button otherwise we render just the column name either taken from Data Annotations or from the column definition.

For all columns marked as Hidden as usual we have:

else if (column.DispalyTemplate != null)
            {
                hasCustomField = true;
            }

and finally:

@if ((options.Features & GridFeatures.Edit) == GridFeatures.Edit
            || hasCustomField
            || (options.Features & GridFeatures.Delete) == GridFeatures.Delete)
        {
            <td class="Theme-DataGrid-Header"></td>
        }

If there is the column for the command buttons, we just render an empty <td> to have the right number of <td>.in the header.

Now we are ready to render the DataGrid:

<table>
@DisplayHeader(options.HtmlHelper, options)
@DataGridHelpers.DataGridFor(options.HtmlHelper,
    options.ToShow,
    ItemContainerType.tr,
    editTemplate,
    displayTemplate,
    null,
    insertTemplate,
    enableMultipleInsert: (options.Features & GridFeatures.InsertOne) != GridFeatures.InsertOne,
    itemCss: "Theme-DataGrid-ItemCss",
    altItemCss: "Theme-DataGrid-AlternateItemCss",
    toTrack: options.GetToTrack())
</table>

toTrack contains the list of all properties we need to save in hidden fields for the changes tracking performed by the grid. This argument is optional, but if we don’t pass it the DataGrid helper just save the original values of all properties of each item. Since, the list of all properties we are using can be easily computed from the columns definition, we get it by calling the GridDescription class method GetToTrack().

Finally we add Pager, and Sorting helper if these services are required:

@if ((options.Features & GridFeatures.Paging) == GridFeatures.Paging)
{
    <div class="Theme-DataGrid-Pager">
        @{ var pager = PagerHelper.PagerFor(options.HtmlHelper, options.Page, options.PrevPage, options.PageCount);}
        @pager.PageButton(ThemedControlsStrings.Get("Page_First", "DataGrid"), PageButtonType.First, PageButtonStyle.Link)
        @pager.PageButton(ThemedControlsStrings.Get("Page_Prev", "DataGrid"), PageButtonType.Previous, PageButtonStyle.Link)
        @pager.PageChoice(5)
        @pager.PageButton(ThemedControlsStrings.Get("Page_Next", "DataGrid"), PageButtonType.Next, PageButtonStyle.Link)
        @pager.PageButton(ThemedControlsStrings.Get("Page_Last", "DataGrid"), PageButtonType.Last, PageButtonStyle.Link)
    </div>
}

@if (((options.Features & GridFeatures.Sorting) == GridFeatures.Sorting) && options.ToOrder != null)
{
        @DataGridHelpers.EnableSortingFor(
            options.HtmlHelper, options.ToShow,
            options.ToOrder, "Theme-DataGrid-NormalHeader",
            "Theme-DataGrid-AscendingHeader", "Theme-DataGrid-DescendingHeader",
            page: options.Page)
}

The whole code can be downloaded from the download page of the Mvc Controls Toolkit here.

That’s all! Not so difficult, isn’t it?

The full code is contained in the RazorThemedGrid file in the Mvc Controls Toolkit download page. Download it and enjoy!

                                                    Stay Tuned

                                                                 Francesco

Tags: , , , , ,

Feb 6 2011

Data Filtering, in the New Mvc 3 Version of the Mvc Controls Toolkit

Category: Entity Framework | MVC | Asp.netFrancesco @ 05:11

See also an updated version of this post:  Advanced Data Filtering Techniques in the Mvc Controls Toolkit

Mvc Controls Toolkit Datagrid Updated Tutorial (updated version of Defining MVC Controls 2: Using the DataGrid)

Defining MVC Controls 3: Datagrid, Sorting, and Master-Detail Views

The Mvc Controls Toolkit offers an interesting data filtering feature: the controller can receive directly a LinQ expression defining the filtering criteria chosen by the user when a view is posted. This way the user can choose among several filtering options by clicking buttons, or selecting checkboxes and then filling the input fields of the selected filtering options. No need to define ViewModel properties for each of the available options, the action method of the controller receives always the same data structure: a LinQ Expression. New filtering options can be easily added modularly with the no need to modify the controller and the code behind it. This increases modularity and makes easier  software maintenance.

Let see how this works in practice with the help of the same example used in my previous posts about the Mvc Controls Toolkit library(see the links next to the title). The whole code used in this example can be downloaded here(the file named BasicTutorialsCode), while screenshots of the software running are here. .

First of all we add the property destined to contain the LinQ expression to the ViewModel:

         public int TotalPages { get; set; }
        public int CurrPage { get; set; }
        public int PrevPage { get; set; }
        public List<Tracker<ToDoView>> ToDoList {get; set;}
        public List<KeyValuePair<LambdaExpression, OrderType>> ToDoOrder { get; set; }
        public System.Linq.Expressions.Expression<Func<ToDoView, bool>> ToDoFilter { get; set; }

The LinQ Expression is a map from ToDoView, that is the class the filter will be applied to, and bool, since formally it defines a function that verifies if an instance of ToDoView satisfies the criterium of the filter.

Now we can use this expression directly in the method that retrieves the items:

if (filter == null)
                {
                    result = context.ToDo.Select(item =>
                        new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).ApplyOrder(order).Select(viewItem =>
                        new Tracker<ToDoView>
                                {
                                    Value = viewItem,
                                    OldValue = viewItem,
                                    Changed = false
                                }).Skip(toSkip).Take(pageDim).ToList();
                }
                else
                {
                    result = context.ToDo.Select(item =>
                        new ToDoView() { Name = item.Name, Description = item.Description, DueDate = item.DueDate, id = item.id }).Where(filter).ApplyOrder(order).Select(viewItem =>
                        new Tracker<ToDoView>
                        {
                            Value = viewItem,
                            OldValue = viewItem,
                            Changed = false
                        }).Skip(toSkip).Take(pageDim).ToList();
                }

The code above shows that it was enough to add a Where(filter) instruction to our original code to get the filtered data.

Now we need to define a class for each filtering option. For sake of semplicity let just define a single filtering option. Our class needs just to implement an interface:

public interface IFilterDescription<TData>
    {
        Expression<Func<TData, bool>> GetExpression();
    }

The GetExpression function returns the actual Filter. The input fields that the user is required to fill are represented by properties of this class, and are used in the implementation of GetExpression():

public class ToDoItemByNameFilter: 
        IFilterDescription<ToDoView>
    {
        [Required]
        [Display(Prompt="chars the name of item starts with")]
        public string Name {get; set;}
        public System.Linq.Expressions.Expression<Func<ToDoView, bool>> GetExpression()
        {
            System.Linq.Expressions.Expression<Func<ToDoView, bool>> res = null;
            Name=Name.Trim();
            if (!string.IsNullOrEmpty(Name))
            {
                Name=Name.Trim();
                res= m => (m.Name.StartsWith(Name));
                
            }
            return res;
        }
    }

In our case we would like to select all items whose Name field starts with the characters inserted in the Name property defined in our ToDoByNameFilter class.

We just Trim our input and then we use it in the LinQ expression returned by GetExpression.

Our ToDoItemByNameFilter plays also the role of ViewModel for a partial view that takes care of the user interface for our filter, so we can decorate its Name property with attributes defining both validation and appearance constraints. In our case we defined a WatermarK through the Prompt property of the Display attribute.

In order to make the watermark actually apppears and the textbox add some adequate formatting we can use the TypedTextBox of the Mvc Controls Toolkit in the partial view that defines the graphical appearance of our filter:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Mvc_Examples.Controls.ToDoItemByNameFilter>" %>
<%@ Import Namespace=" MVCControlsToolkit.Core" %>
<%@ Import Namespace=" MVCControlsToolkit.Controls" %>
<%: Html.TypedTextBoxFor(m => m.Name, watermarkCss:"watermark") %>

Now it remains just to insert the helper of the filter in our View. In order to let the user selects among several filters we can use the ViewList defined in the Mvc Controls Toolkit. This way either by clicking some mutual exclusive checkboxes or by clicking some button, the user let the Partial View associate to a Filter to appear. In our case for sake of simplicity we give to the user just the choice between our previous Name filter and no filter at all:

<div >
    <%: Html.ViewList("ToDoFilter", "ToDoFilterSelected") %> 
        <input id="Checkbox1" type="checkbox" class = 'ToDoFilter_checkbox ByNameFilter_checkbox'/>  &nbsp; Filter by name 
        <span id='ByNameFilter' class='ToDoFilter'><%:Html.DataFilter(m => m.ToDoFilter, 
                                new Mvc_Examples.Controls.ToDoItemByNameFilter(),
                                           "ToDoFilterByName")%></span>  
    </div>
    <div >
    <input id="Checkbox2" type="checkbox" class = 'ToDoFilter_checkbox NoFilter_checkbox'/>  &nbsp; NoFilter
    <span id='NoFilter' class='ToDoFilter'></span>
    </div>

The arguments of our DataFilter helper are just an expression that selects the ViewModel property to fill with the filtering expression, an instance of the class that defines the filter, and the name of the Partial View that we defined before.

The ViewList helper selects one of two spans, one contains the filter helper and the other is simply empty. The selection is done with two checkboxes.Details on how to use of the VieList can be found in the documentation here.

By adding a new argument to our DetailView Helper we can make also our detail view appears in a jQuery UI dialog:

<% Html.DetailFormFor(Ajax, m => m.ToDoList, ExternalContainerType.div,
           "ToDoSubTasks", "Home", null, "isChangedToDo", "isDeletedToDo", detailDialog:
           new MVCControlsToolkit.Controls.DataGrid.Dialog
           {
               Title = "prova dialogo",
               Show = "slide",
               Hide = "slide",
               MinWidth=800

           });%>
</asp:Content>

THAT’S ALL!  Download the code and enjoy!

                             

                                       Stay Tuned!

                                               Francesco

Tags: , , , , , ,