Create a Custom Designer with a Categories Filter
I am creating a custom designer for a widget that will display News content items among other things.
I would like to allow filtering of News content items by Category (as the normal News widget does). Is there any documentation describing how to do this?
So far I've identified some code to put in the designer template:
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sitefinity" Namespace="Telerik.Sitefinity.Web.UI" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="designers" Namespace="Telerik.Sitefinity.Web.UI.ControlDesign" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sfFields" Namespace="Telerik.Sitefinity.Web.UI.Fields" %>
<
sfFields:FormManager
id
=
"formManager"
runat
=
"server"
/>
<
div
>
<
h2
>
<
asp:Literal
ID
=
"choicesTitle"
runat
=
"server"
Text="<%$Resources:Labels, WhichNewsToDisplay %>" /></
h2
>
<
ul
class
=
"sfRadioList"
>
<
li
>
<
asp:RadioButton
runat
=
"server"
ID
=
"contentSelect_AllItems"
Checked
=
"true"
GroupName
=
"ContentSelection"
Text="<%$Resources:Labels, AllPublishedNews %>" />
</
li
>
<
li
>
<
asp:RadioButton
runat
=
"server"
ID
=
"contentSelect_SimpleFilter"
GroupName
=
"ContentSelection"
Text="<%$Resources:Labels, SelectionOfNews %>" />
<
div
id
=
"selectorsPanel"
>
<
designers:FilterSelector
ID
=
"filterSelector"
runat
=
"server"
AllowMultipleSelection
=
"true"
ItemsContainerTag
=
"ul"
ItemTag
=
"li"
ItemsContainerCssClass
=
"sfCheckListBox sfExpandedPropertyDetails"
DisabledTextCssClass
=
"sfTooltip"
>
<
Items
>
<
designers:FilterSelectorItem
ID
=
"FilterSelectorItem1"
runat
=
"server"
Text="<%$Resources:Labels, ByCategories %>"
GroupLogicalOperator="AND" ItemLogicalOperator="OR" ConditionOperator="Contains"
QueryDataName="Categories" QueryFieldName="Category" QueryFieldType="System.Guid">
<
SelectorResultView
>
<
sitefinity:HierarchicalTaxonSelectorResultView
ID
=
"HierarchicalTaxonSelectorResultView1"
runat
=
"server"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc"
AllowMultipleSelection
=
"true"
>
</
sitefinity:HierarchicalTaxonSelectorResultView
>
</
SelectorResultView
>
</
designers:FilterSelectorItem
>
<
designers:FilterSelectorItem
ID
=
"FilterSelectorItem2"
runat
=
"server"
Text="<%$Resources:Labels, ByTags %>"
GroupLogicalOperator="AND" ItemLogicalOperator="OR" ConditionOperator="Contains"
QueryDataName="Tags" QueryFieldName="Tags" QueryFieldType="System.Guid">
<
SelectorResultView
>
<
sitefinity:FlatTaxonSelectorResultView
ID
=
"FlatTaxonSelectorResultView1"
runat
=
"server"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/FlatTaxon.svc"
AllowMultipleSelection
=
"true"
>
</
sitefinity:FlatTaxonSelectorResultView
>
</
SelectorResultView
>
</
designers:FilterSelectorItem
>
</
Items
>
</
designers:FilterSelector
>
</
div
>
</
li
>
<
li
style
=
"display: none;"
>
<
asp:RadioButton
runat
=
"server"
Enabled
=
"false"
ID
=
"contentSelect_AdvancedFilter"
GroupName
=
"ContentSelection"
Text="<%$Resources:Labels, AdvancedSelection %>" /><
asp:Literal
ID
=
"Literal1"
runat
=
"server"
Text="<%$Resources:Labels, InProcessOfImplementation %>" />
</
li
>
</
ul
>
</
div
>
Hi Antoine,
You can modify the control as shown below ( only for categories)
<
designers:FilterSelector
ID
=
"filterSelector"
runat
=
"server"
AllowMultipleSelection
=
"true"
ItemsContainerTag
=
"ul"
ItemTag
=
"li"
ItemsContainerCssClass
=
"sfCheckListBox sfExpandedPropertyDetails"
DisabledTextCssClass
=
"sfTooltip"
>
<
Items
>
<
designers:FilterSelectorItem
ID
=
"FilterSelectorItem1"
runat
=
"server"
Text="<%$Resources:Labels, ByCategories %>"
GroupLogicalOperator="AND" ItemLogicalOperator="OR" ConditionOperator="Contains"
QueryDataName="Categories" QueryFieldName="Category" QueryFieldType="System.Guid">
<
SelectorResultView
>
<
sitefinity:HierarchicalTaxonSelectorResultView
ID
=
"HierarchicalTaxonSelectorResultView1"
runat
=
"server"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc"
AllowMultipleSelection
=
"true"
>
</
sitefinity:HierarchicalTaxonSelectorResultView
>
</
SelectorResultView
>
</
designers:FilterSelectorItem
>
</
Items
>
</
designers:FilterSelector
>
protected
virtual
FilterSelector filterSelector
get
return
this
.Container.GetControl<FilterSelector>(
"filterSelector"
,
true
);
filterSelector.SetTaxonomyId("Categories", TaxonomyManager.CategoriesTaxonomyId);
Thank you Ivan. I put the code you instructed me to add in my ControlDesignerBase subclass and everything is working.
In a related issue, I am trying to get the FlatTaxonField control working.
Eventually I would like to set it up with a new flat classification that I created, but for now, I am just trying to get it to work with the out-of-the-box Tags classification.
In my custom designer .aspx, I have the following:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TabbedDocumentCollectionDesignerTemplate.ascx.cs"
Inherits="SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesignerTemplate" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sitefinity" Namespace="Telerik.Sitefinity.Web.UI" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="designers" Namespace="Telerik.Sitefinity.Web.UI.ControlDesign" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sfFields" Namespace="Telerik.Sitefinity.Web.UI.Fields" %>
<
sfFields:FormManager
ID
=
"formManager"
runat
=
"server"
/>
<
div
class
=
"sfContentViews sfSingleContentView"
>
<
div
class
=
"sfTxtFieldCtrl"
>
<
sfFields:FlatTaxonField
ID
=
"fieldDocumentCollection"
DisplayMode
=
"Read"
BindOnServer
=
"true"
runat
=
"server"
WebServiceUrl
=
"~/Sitefinity/Services/Taxonomies/FlatTaxon.svc"
Expanded
=
"true"
TaxonomyMetafieldName
=
"DocumentCollectionsForDisplay"
Title
=
"Document Collections"
HideWhenNoTaxaFound
=
"false"
CssClass
=
"sfpostTagsWrp"
/>
</
div
>
</
div
>
Type.registerNamespace(
"SitefinityWebApp.Widgets.TabbedDocumentCollection"
);
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner =
function
(element)
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner.initializeBase(
this
, [element]);
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner.prototype =
initialize:
function
()
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner.callBaseMethod(
this
,
'initialize'
);
,
dispose:
function
()
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner.callBaseMethod(
this
,
'dispose'
);
,
refreshUI:
function
()
var
data =
this
._propertyEditor.get_control();
jQuery(
"#fieldDocumentCollection"
).val(data.DocumentCollectionsForDisplay);
,
applyChanges:
function
()
var
controlData =
this
._propertyEditor.get_control();
controlData.DocumentCollectionsForDisplay = jQuery(
"#fieldDocumentCollection"
).val();
SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner.registerClass(
'SitefinityWebApp.Widgets.TabbedDocumentCollection.TabbedDocumentCollectionDesigner'
,
Telerik.Sitefinity.Web.UI.ControlDesign.ControlDesignerBase);
if
(
typeof
(Sys) !==
'undefined'
) Sys.Application.notifyScriptLoaded();
protected override void InitializeControls(Telerik.Sitefinity.Web.UI.GenericContainer container)
base.DesignerMode = ControlDesignerModes.Simple;
TaxonomyManager taxonomyManager = TaxonomyManager.GetManager();
FlatTaxonomy taxonomyDocumentCollection = taxonomyManager.GetTaxonomies<FlatTaxonomy>().Where(t => t.Name ==
"Tags"
).SingleOrDefault();
this
.DocumentCollectionsField.TaxonomyId = taxonomyDocumentCollection.Id;
protected virtual FlatTaxonField DocumentCollectionsField
get
return
this
.Container.GetControl<FlatTaxonField>(
"fieldDocumentCollection"
,
true
);
Hi Antoine,
You need to supply TaxonomyId . Currently the control calls GetTaxonomy
but you do not pass any value there, so it should be bound properly.
Greetings,
Ivan Dimitrov
the Telerik team
Hello Ivan
I am setting the TaxonomyId in InitializeControls of my ControlDesignerBase:
TaxonomyManager taxonomyManager = TaxonomyManager.GetManager();
FlatTaxonomy taxonomyDocumentCollection = taxonomyManager.GetTaxonomies<FlatTaxonomy>()
.Where(t => t.Name ==
"Tags"
)
.SingleOrDefault();
this
.DocumentCollectionsField.TaxonomyId = taxonomyDocumentCollection.Id;
Hello Antoine,
This looks fine. Do you get the control populated with data? In most of the places we set the property in the template directly.
Greetings,
Ivan Dimitrov
the Telerik team
Hello guys,
I don't find the answer for the original question : "How do I save the option values for the widget instance? Do I define a ContentSelection property and Category property for the widget?".
I'm able to display and select taxonomies (Tags/Categories) but in the javascript of my Desginer :
- I need to retrieve the tags/categories selected in the FilterSelector (applyChanges function) but I don't know how
- I need to set FilterSelector with the tags/categories that were selected before (refreshUI function).
Can somenone help me with this?
Thanks a lot for your help.
Jonathan.
Hi Jonathan
For various reasons, I dropped this task and thus never got it working. If you are able to, I would be glad to see your solution in this forum post.
Thanks,
Antoine
I have the same issues... I can't figure out how to get to pass the values back and forth between my control and the designer using applyChanges and refreshUI...
Hi All,
I opened a ticket about this issue and here's the answer I got from the support.
Hi Jonathan,
Thank you for the patience.I have explored several options for achieving this functionality, please find below my recommendations:
1. I'd recommend you to switch the implementation from FilterSelector
to ChoiceField - you can customize the frontend representation by specifying myControl.RenderChoicesAs = Telerik.Sitefinity.Web.UI.Fields.Enums.RenderChoicesAs.(specify desired view)
An example of CHoice field implementation in a control designer can be found in the latest release of our SDK in the TaxonomyDropDown of the Products Catalog module.
You can define the ChoiceField in your template as:
<%@ Control Language="C#" %>
<%@ Register Assembly="Telerik.Sitefinity" TagPrefix="sitefinity" Namespace="Telerik.Sitefinity.Web.UI" %>
<%@ Register Assembly="Telerik.Web.UI, Version=2011.1.413.40" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
<%@ Register Assembly="Telerik.Sitefinity, Version=4.1.1395.0" Namespace="Telerik.Sitefinity.Web.UI.Fields" TagPrefix="sfFields" %>
<
ul
class
=
"sfRadioList sfTitledList"
>
<
li
>
<
sfFields:ChoiceField
ID
=
"hidePrice"
runat
=
"server"
CssClass
=
"sfInlineWrapper"
DataFieldName
=
"HidePriceOnFrontend"
DisplayMode
=
"Write"
RenderChoicesAs
=
"SingleCheckBox"
>
</
sfFields:ChoiceField
>
</
li
>
</
ul
>
protected
override
void
InitializeControls(GenericContainer container)
var tManager = TaxonomyManager.GetManager();
// var tid = tdefinition.TaxonomyId;
var allTags = tManager.GetTaxa<FlatTaxon>();
//.GetTaxonomies<FlatTaxonomy>();
if
(allTags !=
null
)
var myControl =
this
.Container.GetControl<ChoiceField>(
"hidePrice"
,
true
);
myControl.RenderChoicesAs = Telerik.Sitefinity.Web.UI.Fields.Enums.RenderChoicesAs.CheckBoxes;
//
// or you can use Telerik.Sitefinity.Web.UI.Fields.Enums.RenderChoicesAs.CheckBoxes for multiple choice
myControl.Choices.Clear();
foreach
(var taxon
in
allTags)
var choice =
new
ChoiceItem();
choice.Value = taxon.Id.ToString();
choice.Text = taxon.Title;
choice.Enabled =
true
;
myControl.Choices.Add(choice);
public
override
IEnumerable<ScriptDescriptor> GetScriptDescriptors()
var descriptor =
new
ScriptControlDescriptor(
typeof
(DatePickerDesignerView).FullName,
this
.ClientID);
descriptor.AddComponentProperty(
"minimumPicker"
,
this
.MinimumPicker.ClientID);
descriptor.AddComponentProperty(
"maximumPicker"
,
this
.MaximumPicker.ClientID);
descriptor.AddComponentProperty(
"hidePrice"
,
this
.Container.GetControl<ChoiceField>(
"hidePrice"
,
true
).ClientID);
return
new
[] descriptor ;
public
Guid[] MyTagSelection
get
if
(
this
.myTagSelection ==
null
)
this
.myTagSelection =
new
Guid[];
return
this
.myTagSelection;
set
this
.myTagSelection = value;
private
Guid[] myTagSelection;
refreshUI: function ()
debugger;
var controlData =
this
.get_controlData();
var myTagSel = controlData.MyTagSelection;
this
.set_hidePrice(myTagSel);
,
// implementation of IDesignerViewControl: forces the designer view to apply the changes on UI to the control Data
applyChanges: function ()
var controlData =
this
.get_controlData();
debugger;
var tagSelection =
this
._hidePrice.get_value();
controlData.MyTagSelection = tagSelection;
,
I've gotten the FilterSelector to work for categories successfully in a control designer(thanks to a little help from inspecting the Sitefinity libraries with JustDecompile). Start out as Ivan specified in his first post above. In the control designer CS file you will also need to register the ScriptDescriptor for the filter selector:
public
override
IEnumerable<ScriptDescriptor> GetScriptDescriptors()
var baseDescriptors =
new
List<ScriptDescriptor>(
base
.GetScriptDescriptors());
var newDescriptors = (ScriptControlDescriptor)baseDescriptors.Last();
newDescriptors.AddComponentProperty(
"filterSelector"
,
this
.filterSelector.ClientID);
return
baseDescriptors;
get_filterSelector:
function
()
return
this
._filterSelector;
,
set_filterSelector:
function
(value)
this
._filterSelector = value;
,
refreshUI:
function
()
var
controlData =
this
.get_controlData();
var
additionalFilters =
this
.get_controlData().CategoryJSON;
if
(additionalFilters)
additionalFilters = Sys.Serialization.JavaScriptSerializer.deserialize(additionalFilters);
this
.get_filterSelector().set_queryData(additionalFilters);
,
applyChanges:
function
()
var
controlData =
this
.get_controlData();
var
filterSelector =
this
.get_filterSelector();
filterSelector.applyChanges();
var
queryData = filterSelector.get_queryData();
if
(queryData.QueryItems && queryData.QueryItems.length > 0)
queryData = Telerik.Sitefinity.JSON.stringify(queryData);
else
queryData =
null
;
controlData.CategoryJSON = queryData;
,
// Technically, this variable would hold any of the filter data returned from the FilterSelector,
// but it is only configured for Categories, so I'm naming the variable as such
public
string
CategoryJSON
get
;
set
;
protected
List<Guid> Categories
get
List<Guid> categoryGUIDList =
new
List<Guid>();
if
(!
string
.IsNullOrEmpty(CategoryJSON))
//Parse the JSON string and extract the category IDs
QueryData qData = (
new
JavaScriptSerializer()).Deserialize<QueryData>(CategoryJSON);
List<QueryItem> categoryNames = qData.QueryItems.Where(qi => qi.Condition !=
null
&& qi.Condition.FieldName ==
"Category"
).ToList<QueryItem>();
foreach
(QueryItem qi
in
categoryNames)
categoryGUIDList.Add(Guid.Parse(qi.Value));
return
categoryGUIDList;
using
Telerik.Sitefinity.Web.Model;
using
System.Web.Script.Serialization;
Hi,
Thank you, Alan for sharing your solution.
Regards,
Stefani Tacheva
Telerik