ASP.NET Dynamic Data Filtering - DynamicFilterRepeater
One of the new features I released with Dynamic Data Filtering 1.10 is the DynamicFilterRepeater control. This control was added based on requests from several people who have implemented Dynamic Data Filtering in their applications. The idea is to use the model metadata to generate the set of filtering controls on each page as opposed to using the DynamicFilterForm. This is a great addition because it allows developers to annotate their model and modify their PageTemplates to get full Dynamic Filtering capabilities throughout the site.
The first step in using the DynamicFilterRepeater is to replace the existing FilterRepeater on either the DynamicData\PageTemplates\List.aspx or DynamicData\PageTemplates\ListDetails.aspx page.
In this example we will focus on the List.aspx page. On the List.aspx page, the default FilterRepeater controls looks something like:
<asp:FilterRepeater ID="FilterRepeater" runat="server">
<ItemTemplate>
<asp:Label runat="server" Text='<%# Eval("DisplayName") %>' AssociatedControlID="DynamicFilter$DropDownList1" />
<asp:DynamicFilter runat="server" ID="DynamicFilter" OnSelectedIndexChanged="OnFilterSelectedIndexChanged" />
</ItemTemplate>
<FooterTemplate><br /><br /></FooterTemplate>
</asp:FilterRepeater>
Delete it!
Next drag the DynamicFilterRepeater from the tool box on to the form. You will get markup that looks like the following:
Tip: I HIGHLY recommend you do this in the designer view. If you do you will get prompted to create the FilterTemplates directory, if it is not already created. References to Catalyst.Web.DynamicData and Catalyst.ComponentModel.DataAnnotations will also be added.
<asp:DynamicFilterRepeater ID="DynamicFilterRepeater1" runat="server">
<HeaderTemplate>
<div>
Search</div>
</HeaderTemplate>
<ItemTemplate>
<div>
<asp:Label ID="Label1" runat="server" Text='<%# Eval("DisplayName") %>'></asp:Label><asp:DynamicFilterControl
ID="DynamicFilter" runat="server">
</asp:DynamicFilterControl>
</div>
</ItemTemplate>
<FooterTemplate>
<div>
<asp:LinkButton ID="SearchButton" runat="server" CommandName="Search" Text="Search">
</asp:LinkButton><asp:LinkButton ID="ClearButton" runat="server" CommandName="Clear"
Text="Clear">
</asp:LinkButton></div>
</FooterTemplate>
</asp:DynamicFilterRepeater>
This is the default template for items within the DynamicFilterRepeater and is very similar to the FilterRepeater we just deleted. You can customize this, but you must, MUST, MUST have one and only one DynamicFilterControl with the ID="DynamicFilter" in the ItemTemplate. The DynamicFilterRepeater container uses this to create your filtering controls.
After we have the DynamicFilterRepeater control completed, we need to replace the LinqDataSource with a DynamicLinqDataSource. The existing LinqDataSource should look something like this:
<asp:LinqDataSource ID="GridDataSource" runat="server" EnableDelete="true" EnableUpdate="true">
<WhereParameters>
<asp:DynamicControlParameter ControlID="FilterRepeater" />
</WhereParameters>
</asp:LinqDataSource>
Don't delete it. Switch to the designer view and select your DynamicFilterRepeater and then the Actions pop-up arrow. This will allow you to select a DataSource. Select your target datasource, in this case GridDataSource, from the drop down list. You will see an action called UpgradeData source appear in the Action popup, hit it. This will automatically change your LinqDataSource to a DynamicLinqDataSource, pretty cool, huh?
Your data source should now look like this (notice the removal of the DynamicControlParameter tag!):
<asp:DynamicLinqDataSource ID="GridDataSource" runat="server"
EnableDelete="True" EnableUpdate="True">
</asp:DynamicLinqDataSource>
If you dragged the DynamicFilterRepeater onto the form in DesignView, it will automatically have added a reference to Catalyst.ComponentModel.DataAnnotations, if not you must add a reference. It should be in the list under the .NET tab, otherwise browse to %PROGRAMFILES%\Dynamic Data Filtering\bin.
The final step is to modify the metamodel. Since the samples provided with the installer use the AdventureWorksLT2008 database I am going to annotate the Product class. This is an example of the Product class and annotations.
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Catalyst.ComponentModel.DataAnnotations;
namespace AdventureWorks.BusinessObjects
{
[System.ComponentModel.DataAnnotations.MetadataType(typeof(ProductMetadata))]
public partial class Product
{
/// <summary>
/// This is the Metadata for the parent Product class. It is a private interface to prevent
/// consumers from attempting to instantiate it or even see it.
/// </summary>
private interface ProductMetadata
{
[Filter(FilterMode = FilterControlMode.Contains)]
string Name { get; set; }
[Filter(FilterMode = FilterControlMode.Contains)]
[DisplayName("Product Number")]
string ProductNumber { get; set; }
[Filter(FilterMode = FilterControlMode.Contains)]
string Color { get; set; }
[Filter(FilterMode = FilterControlMode.Range)]
[DisplayName("List Price")]
decimal ListPrice { get; set; }
[Filter(FilterMode = FilterControlMode.Equals)]
[DisplayName("Product Model")]
ProductModel ProductModel { get; set; }
[Filter(FilterMode=FilterControlMode.Equals)]
[DisplayName("Product Category")]
ProductCategory ProductCategory { get; set; }
[ScaffoldColumn(false)]
Guid rowguid { get; set; }
[ScaffoldColumn(false)]
DateTime ModifiedDate { get; set; }
}
}
}
One thing to note in this example is that I have made the Metadata class a private nested interface of the Product class. I have done this to prevent visibility from outside classes and also to prevent any code within the Product class from attempting to instantiate a instance of that type. While giving a presentation last week, Scott Seely asked if this was possible, we tried it on the stop and it worked like a charm.
Now run the solution and you should have FilterControls defined for List.aspx page template.