Create a Custom Designer with a Categories Filter

Posted by Community Admin on 05-Aug-2018 17:47

Create a Custom Designer with a Categories Filter

All Replies

Posted by Community Admin on 02-May-2011 00:00

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>

Some specific issues I need to resolve:
How do I configure the 'FilterSelectorItem' control so that the 'Select a category' dialog displays a list of categories to choose from?
How do I save the option values for the widget instance?  Do I define a ContentSelection property and Category property for the widget?

Posted by Community Admin on 04-May-2011 00:00

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>

Inside your control designer you should make a reference to the control

protected virtual FilterSelector filterSelector
      
          get
          
              return this.Container.GetControl<FilterSelector>("filterSelector", true);
          
      


and cal SetTaxonomyID method inside InitializeControls

filterSelector.SetTaxonomyId("Categories", TaxonomyManager.CategoriesTaxonomyId);

The selected value should be returned as an additional field on the client.

Greetings,
Ivan Dimitrov
the Telerik team
Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 04-May-2011 00:00

Thank you Ivan.  I put the code you instructed me to add in my ControlDesignerBase subclass and everything is working.

Posted by Community Admin on 05-May-2011 00:00

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>

In the javascript designer file, I have the following:
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();

In my ControlDesignerBase subclass, I have added:
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);
    

When I run the custom designer, the FlatTaxonField title is displayed, but no categories are displayed.  Is there a step(s) in initializing the control that I am missing?

Also, I want the user's classification selection to be saved to the widget instance.  If I understand correctly, the 'TaxonomyMetafieldName' property of the FlatTaxonField specified the widget instance property that the classification selections are loaded from and saved to.  Is that correct?  What should the datatype of this property be in the widget?

Thanks!
Antoine

Posted by Community Admin on 06-May-2011 00:00

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

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 06-May-2011 00:00

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;

Is there some other way I should bind the property?

Antoine

Posted by Community Admin on 11-May-2011 00:00

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

Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>

Posted by Community Admin on 26-Jul-2011 00:00

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.

Posted by Community Admin on 26-Jul-2011 00:00

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

Posted by Community Admin on 19-Aug-2011 00:00

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...

Posted by Community Admin on 19-Aug-2011 00:00

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>

and then populate the Choices in the codefile by getting all Taxa with the API:

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);
               
           
       

please note to add the copmponent ScriptDescriptors:

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 ;
        

I believe this implementation would be much easier. Concerning your inquiries about saving and loading the selection of tags, I'd suggest you examine the ChoiceField implementation in the products catalog sample, where a public property is exposed in your control and you use controlData object to persist the selection.
For example:


public Guid[] MyTagSelection
        
            get
            
                if(this.myTagSelection == null)
                    this.myTagSelection = new Guid[];
                return this.myTagSelection;
            
            set
            
                this.myTagSelection = value;
            
        
  
        private Guid[] myTagSelection;

and the controlData implementation:

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;
    ,


Voila!

I know that it's just a workaround but it worked for me.

Good luck with your dev!

Jonathan.

Posted by Community Admin on 14-Aug-2013 00:00

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;

Then, for the methods in the control designer JS file:
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;
,

The values of the FilterSelector control are now set up to be persisted, but we need to make these accessible on the widget being designed.  On the widget being designed, I added the following properties:
// 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;
    

You will also need to include the following namespaces in your widget class:
using Telerik.Sitefinity.Web.Model;
using System.Web.Script.Serialization;

Now the categories selected in the designer can be accessed as a nice List<Guid>  in your custom widget. Enjoy!

Alan

Posted by Community Admin on 19-Aug-2013 00:00

Hi,

Thank you, Alan for sharing your solution.

Regards,
Stefani Tacheva
Telerik

Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

This thread is closed