Filtering Sitefinity ContentView-based Controls on the Frontend

Filtering Sitefinity ContentView-based Controls on the Frontend

Posted on October 18, 2013 0 Comments

The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.

Sitefinity's ContentView class, and all its inheritors (News, Blog Posts, Events, etc. widgets) are among the most popular subjects discussed in our blog post. And there's a pretty good reason for that, after all - our ContentView widgets are one of the most important elements that build the Sitefinity experience - they help you select the data you want to display and its behavior on the frontend.

Keeping in mind the numerous use case scenarios ContentView controls cover, you'd be surprised at how much they can do in addition if you plug in the right places and implement your own business logic.

Today we're going to demonstrate how one can inherit from a ContentView widget and directly manipulate its DataSource in order to achieve frontend filtering.

Let's take News for example.  As every other ContentView widget the NewsView widget also has a Master and Details view. Since we want to filter the list of items, we'll need to modify its List of news template and inherit from its MasterView class.

1. You need to create a new UserControl based on the default News widget list template.
In this new template you can define the controls that will handle the presentational part of your filtering mechanism. For example a TextBox/DropDownList that you will populate with the filter options, and a Button that will execute the filtering. For example:
<%@ Control Language="C#" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.ContentUI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.Comments" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %>
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit" Assembly="Telerik.Sitefinity" %>
 
<telerik:RadComboBox runat="server" ID="categoriesCombo" Skin="Sitefinity" />
<asp:Button runat="server" ID="filterBtn" Text="Filter" />
 
<telerik:RadListView ID="NewsList" ItemPlaceholderID="ItemsContainer" runat="server" EnableEmbeddedSkins="false" EnableEmbeddedBaseStylesheet="false">
    <LayoutTemplate>
        <sf:ContentBrowseAndEditToolbar ID="MainBrowseAndEditToolbar" runat="server" Mode="Add"></sf:ContentBrowseAndEditToolbar>
        <ul class="sfnewsList sfnewsListTitleDateSummary">
            <asp:PlaceHolder ID="ItemsContainer" runat="server" />
        </ul>
    </LayoutTemplate>
    <ItemTemplate>
        <li class="sfnewsListItem">
            <h2 class="sfnewsTitle">
                <sf:DetailsViewHyperLink ID="DetailsViewHyperLink1" TextDataField="Title" ToolTipDataField="Description" runat="server" />
            </h2>
            <div class="sfnewsMetaInfo">
                <sf:FieldListView ID="PublicationDate" runat="server" Format="{PublicationDate.ToLocal():MMM dd, yyyy}" />
            </div>
 
            <sf:FieldListView ID="summary" runat="server" Text="{0}" Properties="Summary" WrapperTagName="div" WrapperTagCssClass="sfnewsSummary" />
 
            <sf:DetailsViewHyperLink ID="FullStory" Text="<%$ Resources:NewsResources, FullStory %>" runat="server" CssClass="sfnewsFullStory" />
            <sf:ContentBrowseAndEditToolbar ID="BrowseAndEditToolbar" runat="server" Mode="Edit,Delete,Unpublish"></sf:ContentBrowseAndEditToolbar>
        </li>
    </ItemTemplate>
</telerik:RadListView>
<sf:Pager ID="pager" runat="server"></sf:Pager>
<asp:PlaceHolder ID="socialOptionsContainer" runat="server" />

2. Once we've created the custom template we need to inherit from the default Telerik.Sitefinity.Modules.News.Web.UI.MasterListView class.
In the new class we can override MasterListView's LayoutTemplatePath property and point it to the custom template we have already created:
public override string LayoutTemplatePath
       {
           get
           {
               return this.layoutTemplatePath;
           }
           set
           {
               base.LayoutTemplatePath = value;
           }
       }
 
private string layoutTemplatePath = "~/CustomWidgets/News/Views/NewsMasterListViewCustomTemplate.ascx";

3. Next,  you can then get a reference to your controls so you can operate with them:
public virtual RadComboBox CategoriesCombo
       {
 
           get
           {
 
               return this.Container.GetControl<RadComboBox>("categoriesCombo", false);
 
           }
 
       }
 
       public virtual Button FilterButton
       {
 
           get
           {
 
               return this.Container.GetControl<Button>("filterBtn", false);
 
           }
 
       }

4. We can then override the InitializeControls method of the MasterListView, and implement our business logic for populating the filter options.
A common use case scenario we encounter in support is the need to have a categories dropdown, that would allow filtering the list dynamically, so let's go with this one.
In order to ensure we're listing only categories that have been used to mark items of this content type we can query the TaxonomyStatistics - this is where Sitefinity keeps a reference for each taxon about the number of marked items, and their content type. So we can do something like:
protected override void InitializeControls(Telerik.Sitefinity.Web.UI.GenericContainer container, Telerik.Sitefinity.Web.UI.ContentUI.Contracts.IContentViewDefinition definition)
       {
           var dataItemType = "Telerik.Sitefinity.News.Model.NewsItem";
           //Get all taxa that have been used for the particular dataItemType
           TaxonomyManager taxonomyManager = TaxonomyManager.GetManager();
           var newsCategories = taxonomyManager.GetStatistics()
                                   .Where(tS => tS.DataItemType == dataItemType && tS.TaxonomyId == TaxonomyManager.CategoriesTaxonomyId)
                                   .Where(tS => tS.StatisticType == ContentLifecycleStatus.Live && tS.MarkedItemsCount > 0)
                                   .Distinct()
                                   .Select(t => new
                                   {
                                       taxonId = t.TaxonId,
                                       taxonTitle = taxonomyManager.GetTaxon<HierarchicalTaxon>(t.TaxonId).GetString("Title"),
                                   }).ToList();

5. Once we have the datasource ready, we need to bind it to our dropdown:
//bind the RadComboBox to the taxa
            this.CategoriesCombo.DataTextField = "taxonTitle";
            this.CategoriesCombo.DataValueField = "taxonId";
            this.CategoriesCombo.DataSource = newsCategories;
            this.CategoriesCombo.DataBind();
            //Add a default EmptyItem
            RadComboBoxItem emptyItem = new RadComboBoxItem("Choose category");
            this.CategoriesCombo.Items.Insert(0, emptyItem);

Up to this point we have everything we need to display a list of taxa in a dropdown on the list of news items. Our next steps will define our filtering logic.
In order to achieve the best performance we would recommend modifying the default FilterExpression dynamically. The FilterExpression in Sitefinity ContentView widgets is used when constructing the query to the database when we retrieve the data source. This means that by modifying it before the query gets executed, this will ensure we're not making any additional calls to the database, but rather just affecting how the query is constructed, and what items we retrieve from the database.

In order to modify the FilterExpression, and for this modification to take effect we need to do two things:

6. Override the GetItemsList() method - this is where we construct the query and get the items. So we can simply get the selected value from our categories dropdown and apply this value to the filter expression:

protected override IQueryable<Telerik.Sitefinity.News.Model.NewsItem> GetItemsList(ref int? totalCount)
        {
            //check if a category to filter by is selected and add it to the filter expression
            if (this.CategoriesCombo.SelectedIndex > 0)
            {
                this.FilterExpression += string.Format(" AND Category.Contains(({0}))", this.CategoriesCombo.SelectedValue.ToString());
            }
            //populate items list
            return base.GetItemsList(ref totalCount);
        }

7. Due to the specific lifecycle of the ContentView widgets we will need to override the 
OnPreRender() method - this is where we're going to invoke the GetItemsList() and InitializeListView() methods of the ContentView, so we can ensure our filtering takes effect:
protected override void OnPreRender(EventArgs e)
       {
           int? totalCount = 0;
           IQueryable<NewsItem> query = this.GetItemsList(ref totalCount);
           this.InitializeListView(query, totalCount);
           base.OnPreRender(e);
       }

That's it - once you have compiled your project successfully there's one last piece to complete today's exercise - we need to tell Sitefinity to use our custom Master View instead of the default one.

To do that we can take advantage of the concept of Definitions in Sitefinity. They allow us to substitute any existing view with a custom one through the corresponding ContentViewDefinitionElement.
8. In the current case we just need to go to Administration->Settings->Advanced->ContentView->News->News Frontend -> Views-> NewsFrontendList and replace the ViewType property with the CLR type of our custom view. Save the changes and you-re done - from this point on all News widgets throughout your site will use the custom master view instead of the default one.

As usual, here's a sample video demonstrating this functionality.

The complete sample can be downloaded from here:
 
FilterContentViewFrontend

With this we finish today's blog post. Feel free to modify it as per your specific requirements, and we hope you found this information useful.
IMG_0765

Boyan Barnev

Boyan Barnev is a Principal Information Developer at Progress. His mission is to demonstrate the unlimited capabilities of Sitefinity CMS via product documentation, SDK samples, and technical blog posts. He has graduated from the American University in Bulgaria and joined Telerik in 2011. Since then Boyan has held various positions in the company, leading the strategy and operation of the Sitefinity CMS Technical support service worldwide.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation