The scenario for today's blog post is a pretty straightforward one - as a website developer I'd like to offer my frontend users a mechanism for filtering the list of Dynamic Module items by certain criteria.
To represent the idea better, let's say we'll be dealing with Offices - we're an international company that has offices around the globe.
Handling this type of scenario with Sitefinity Module Builder takes not more than 5 minutes - the time in which I defined my new module, added some fields to specify the office Title, Country, City, ZipCode, PhoneNumber etc. fields.
Once we've created the module our next step is to deliver the filtering functionality.
For this purpose we're going to inherit from DynamicContentView, which is the type of the widget ModuleBuilder creates for displaying your new module content:
-------------------------------------------------------------------------------------------------------------------------------
NOTE:
After Sitefinity 6.1 the
-------------------------------------------------------------------------------------------------------------------------------
As you can see from the above sample, our new class overrides just one method of the base DynamicContentView, namely InitializeMasterView(). In InitializeMasterView() we are able to substitute the default MasterViewControl with our custom one - DynamicContentViewMasterCustom, defined above.
Our DynamicContentViewMasterCustom is a new class, inheriting from DynamicContentViewMaster, and will have a customized template that will facilitate the UI for filtering on the frontend.
For the sake of this example I've copied the default template our new module has created (you can access this from Design->WidgetTemplates-> YourModule list) and added the desired controls to it:
In the DynamicContentViewMasterCustom we can now reference these controls, using the this.Container.GetControl<T>() approach:
and then subscribe to the Click event of the SearchButton by overriding the base InitializeControls method:
our final step is to implement the actual filtering of the DataSource according to the user input:
as you can see, DynamicContentListView already has its DataSource set for us, so we can access it, and apply our filtering directly.
Once you're done the last step is registering your new widget. Go to App_Data/Sitefinity/Configuration/ToolboxesConfig.config and find the entry for your module's widget. It should look like this:
Change the type parameter to match the type of your custom DynamicContentView:
Save the file and restart your application - you'll now be able to drop the widget on any Sitefinity page and enjoy the results.
You can find the complete implementation of our DynamicContentViewMasterCustom along with the custom template and DynamicContentViewCustom attached to this post - OfficesModule.
For your convenience please find here a short video demonstrating the final results as well.
I hope you enjoyed reading about this simple functionality, and the ease of achieving it with Sitefinity.
To represent the idea better, let's say we'll be dealing with Offices - we're an international company that has offices around the globe.
Handling this type of scenario with Sitefinity Module Builder takes not more than 5 minutes - the time in which I defined my new module, added some fields to specify the office Title, Country, City, ZipCode, PhoneNumber etc. fields.
Once we've created the module our next step is to deliver the filtering functionality.
For this purpose we're going to inherit from DynamicContentView, which is the type of the widget ModuleBuilder creates for displaying your new module content:
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
Telerik.Sitefinity.DynamicModules.Web.UI.Frontend;
namespace
SitefinityWebApp.OfficesModule
{
public
class
DynamicContentViewCustom : DynamicContentView
{
protected
DynamicContentViewMaster MasterViewControl
{
get
{
if
(
this
.masterViewControl ==
null
)
{
this
.masterViewControl =
new
DynamicContentViewMasterCustom(
this
.DynamicManager);
}
return
this
.masterViewControl;
}
set
{
this
.masterViewControl = value;
}
}
protected
override
void
InitializeMasterView()
{
if
(
this
.HasValidRelatedDataConfiguration &&
this
.RelatedItemsIds !=
null
)
{
this
.MasterViewControl.SourceItemsIds =
this
.RelatedItemsIds;
}
this
.MasterViewControl.TemplateKey =
string
.IsNullOrEmpty(
this
.MasterViewDefinition.TemplateKey) ?
this
.DefaultMasterTemplateKey :
this
.MasterViewDefinition.TemplateKey;
this
.MasterViewControl.DynamicContentType =
this
.DynamicContentType;
this
.MasterViewControl.MasterViewDefinition =
this
.MasterViewDefinition;
this
.MasterViewControl.UrlEvaluationMode =
this
.UrlEvaluationMode;
this
.MasterViewControl.UrlKeyPrefix =
this
.UrlKeyPrefix;
this
.Controls.Add(
this
.MasterViewControl);
}
private
DynamicContentViewMaster masterViewControl;
}
}
NOTE:
After Sitefinity 6.1 the
DynamicContentViewMaster
class now has a Host property which allows you to easily access the DynamicContentView
(which is the Host) for its master/details view. This change requires you to modify the above code slightly, so if you are using Sitefinity 6.1 or higher version of our product, please use this sample instead:using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Web;
using
Telerik.Sitefinity.DynamicModules.Web.UI.Frontend;
namespace
SitefinityWebApp.OfficesModule
{
public
class
DynamicContentViewCustom : DynamicContentView
{
protected
DynamicContentViewMaster MasterViewControl
{
get
{
if
(
this
.masterViewControl ==
null
)
{
this
.masterViewControl =
new
DynamicContentViewMasterCustom(
this
.DynamicManager);
this
.masterViewControl.Host =
this
;
}
return
this
.masterViewControl;
}
set
{
this
.masterViewControl = value;
}
}
protected
override
void
InitializeMasterView()
{
if
(
this
.HasValidRelatedDataConfiguration &&
this
.RelatedItemsIds !=
null
)
{
this
.MasterViewControl.SourceItemsIds =
this
.RelatedItemsIds;
}
this
.MasterViewControl.TemplateKey =
string
.IsNullOrEmpty(
this
.MasterViewDefinition.TemplateKey) ?
this
.DefaultMasterTemplateKey :
this
.MasterViewDefinition.TemplateKey;
this
.MasterViewControl.DynamicContentType =
this
.DynamicContentType;
this
.MasterViewControl.MasterViewDefinition =
this
.MasterViewDefinition;
this
.MasterViewControl.UrlEvaluationMode =
this
.UrlEvaluationMode;
this
.MasterViewControl.UrlKeyPrefix =
this
.UrlKeyPrefix;
this
.Controls.Add(
this
.MasterViewControl);
}
private
DynamicContentViewMaster masterViewControl;
}
}
As you can see from the above sample, our new class overrides just one method of the base DynamicContentView, namely InitializeMasterView(). In InitializeMasterView() we are able to substitute the default MasterViewControl with our custom one - DynamicContentViewMasterCustom, defined above.
Our DynamicContentViewMasterCustom is a new class, inheriting from DynamicContentViewMaster, and will have a customized template that will facilitate the UI for filtering on the frontend.
For the sake of this example I've copied the default template our new module has created (you can access this from Design->WidgetTemplates-> YourModule list) and added the desired controls to it:
<%@ Control Language="C#" %>
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit" Assembly="Telerik.Sitefinity" %>
<%@ 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.Fields" 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" %>
<
span
>City</
span
>
<
br
/>
<
asp:TextBox
ID
=
"cityTextBox"
runat
=
"server"
/>
<
br
/>
<
span
>Country</
span
>
<
br
/>
<
asp:TextBox
ID
=
"countryTextBox"
runat
=
"server"
/>
<
br
/>
<
span
>ZipCode</
span
>
<
br
/>
<
asp:TextBox
ID
=
"zipCodeTextBox"
runat
=
"server"
/>
<
br
/>
<
asp:Button
ID
=
"searchBtn"
Text
=
"Search"
runat
=
"server"
/>
<
telerik:RadListView
ID
=
"dynamicContentListView"
ItemPlaceholderID
=
"ItemsContainer"
runat
=
"server"
EnableEmbeddedSkins
=
"false"
EnableEmbeddedBaseStylesheet
=
"false"
>
<
LayoutTemplate
>
<
ul
class
=
"sfitemsList sfitemsListTitleDateTmb"
>
<
asp:PlaceHolder
ID
=
"ItemsContainer"
runat
=
"server"
/>
</
ul
>
</
LayoutTemplate
>
<
ItemTemplate
>
<
li
class
=
"sfitem sfClearfix"
>
<
h2
class
=
"sfitemTitle"
>
<
sf:DetailsViewHyperLink
ID
=
"DetailsViewHyperLink"
TextDataField
=
"Title"
runat
=
"server"
/>
</
h2
>
<
sf:FieldListView
ID
=
"PublicationDate"
runat
=
"server"
Format
=
"{PublicationDate.ToLocal():MMM d, yyyy, HH:mm tt}"
WrapperTagName
=
"div"
WrapperTagCssClass
=
"sfitemPublicationDate"
/>
</
li
>
</
ItemTemplate
>
</
telerik:RadListView
>
<
sf:Pager
ID
=
"pager"
runat
=
"server"
></
sf:Pager
>
<
asp:PlaceHolder
ID
=
"socialOptionsContainer"
runat
=
"server"
></
asp:PlaceHolder
>
In the DynamicContentViewMasterCustom we can now reference these controls, using the this.Container.GetControl<T>() approach:
#region Control References
protected
virtual
TextBox CityTextBox
{
get
{
return
this
.Container.GetControl<TextBox>(
"cityTextBox"
,
true
);
}
}
protected
virtual
TextBox CountryTextBox
{
get
{
return
this
.Container.GetControl<TextBox>(
"countryTextBox"
,
true
);
}
}
protected
virtual
TextBox ZipCodeTextBox
{
get
{
return
this
.Container.GetControl<TextBox>(
"zipCodeTextBox"
,
true
);
}
}
protected
virtual
Button SearchButton
{
get
{
return
this
.Container.GetControl<Button>(
"searchBtn"
,
true
);
}
}
#endregion
and then subscribe to the Click event of the SearchButton by overriding the base InitializeControls method:
protected
override
void
InitializeControls(Telerik.Sitefinity.Web.UI.GenericContainer container)
{
SearchButton.Click += SearchButton_Click;
base
.InitializeControls(container);
}
our final step is to implement the actual filtering of the DataSource according to the user input:
void
SearchButton_Click(
object
sender, EventArgs e)
{
var dataSource = ((IEnumerable<DynamicContent>)
this
.DynamicContentListView.DataSource);
if
(!
this
.CityTextBox.Text.IsNullOrEmpty())
dataSource = dataSource.Where(itm => itm.GetValue(cityFieldName).ToString().Contains(CityTextBox.Text));
if
(!
this
.CountryTextBox.Text.IsNullOrEmpty())
dataSource = dataSource.Where(itm => itm.GetValue(countryFieldName).ToString().Contains(CountryTextBox.Text));
if
(!
this
.ZipCodeTextBox.Text.IsNullOrEmpty())
dataSource = dataSource.Where(itm => itm.GetValue(zipFieldName).ToString().Contains(ZipCodeTextBox.Text));
this
.DynamicContentListView.DataSource = dataSource.ToList();
}
as you can see, DynamicContentListView already has its DataSource set for us, so we can access it, and apply our filtering directly.
Once you're done the last step is registering your new widget. Go to App_Data/Sitefinity/Configuration/ToolboxesConfig.config and find the entry for your module's widget. It should look like this:
<
add
enabled
=
"True"
type
=
"Telerik.Sitefinity.DynamicModules.Web.UI.Frontend.DynamicContentView, Telerik.Sitefinity"
title
=
"OfficesCustom"
cssClass
=
"sfNewsViewIcn"
moduleName
=
"Offices"
DynamicContentTypeName
=
"Telerik.Sitefinity.DynamicTypes.Model.Offices.Office"
DefaultMasterTemplateKey
=
"3fc7803a-47fd-6966-a5dc-ff0000f47b12"
DefaultDetailTemplateKey
=
"40c7803a-47fd-6966-a5dc-ff0000f47b12"
visibilityMode
=
"None"
name
=
"Telerik.Sitefinity.DynamicTypes.Model.Offices.Office"
/>
Change the type parameter to match the type of your custom DynamicContentView:
<
add
enabled
=
"True"
type
=
"SitefinityWebApp.OfficesModule.DynamicContentViewCustom"
title
=
"OfficesCustom"
cssClass
=
"sfNewsViewIcn"
moduleName
=
"Offices"
DynamicContentTypeName
=
"Telerik.Sitefinity.DynamicTypes.Model.Offices.Office"
DefaultMasterTemplateKey
=
"3fc7803a-47fd-6966-a5dc-ff0000f47b12"
DefaultDetailTemplateKey
=
"40c7803a-47fd-6966-a5dc-ff0000f47b12"
visibilityMode
=
"None"
name
=
"Telerik.Sitefinity.DynamicTypes.Model.Offices.Office"
/>
Save the file and restart your application - you'll now be able to drop the widget on any Sitefinity page and enjoy the results.
You can find the complete implementation of our DynamicContentViewMasterCustom along with the custom template and DynamicContentViewCustom attached to this post - OfficesModule.
For your convenience please find here a short video demonstrating the final results as well.
I hope you enjoyed reading about this simple functionality, and the ease of achieving it with Sitefinity.