+1-888-365-2779
Try Now
More in this section

Forums / Sitefinity SDK / Create a Custom Designer with a Categories Filter

Create a Custom Designer with a Categories Filter

13 posts, 0 answered
  1. Antoine
    Antoine avatar
    45 posts
    Registered:
    13 Jul 2012
    02 May 2011
    Link to this post
    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?
  2. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    25 Nov 2016
    04 May 2011
    Link to this post
    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 >>

  3. Antoine
    Antoine avatar
    45 posts
    Registered:
    13 Jul 2012
    04 May 2011
    Link to this post
    Thank you Ivan.  I put the code you instructed me to add in my ControlDesignerBase subclass and everything is working.
  4. Antoine
    Antoine avatar
    45 posts
    Registered:
    13 Jul 2012
    05 May 2011
    Link to this post
    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
  5. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    25 Nov 2016
    06 May 2011
    Link to this post
    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 >>

  6. Antoine
    Antoine avatar
    45 posts
    Registered:
    13 Jul 2012
    06 May 2011
    Link to this post
    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
  7. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    25 Nov 2016
    11 May 2011
    Link to this post
    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 >>

  8. Jonathan
    Jonathan avatar
    8 posts
    Registered:
    19 Apr 2011
    26 Jul 2011
    Link to this post
    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.
  9. Antoine
    Antoine avatar
    45 posts
    Registered:
    13 Jul 2012
    26 Jul 2011
    Link to this post
    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
  10. Conrad Ehinger
    Conrad Ehinger avatar
    59 posts
    Registered:
    22 Sep 2009
    19 Aug 2011
    Link to this post
    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...
  11. Jonathan
    Jonathan avatar
    8 posts
    Registered:
    19 Apr 2011
    19 Aug 2011
    Link to this post
    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.
  12. OregonTech
    OregonTech avatar
    9 posts
    Registered:
    08 Jan 2009
    14 Aug 2013
    Link to this post
    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
  13. Stefani Tacheva
    Stefani Tacheva avatar
    718 posts
    Registered:
    21 Nov 2016
    19 Aug 2013
    Link to this post
    Hi,

    Thank you, Alan for sharing your solution.

    Regards,
    Stefani Tacheva
    Telerik

    Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.

13 posts, 0 answered