More in this section
Categories
Bloggers
Blogs RSS feed

Creating a Custom Widget Designer Using Sitefinity and Sitefinity Thunder Part 2

by David Cowart

This post is the 2nd part of a 2 part blog post that focuses on how to create Designers using Sitefinity Thunder. Please refer to this blog post to see the first part...

Open up your custom Widget UI file that we created at the beginning of this post, make sure it’s NOT the Designer UI, and add the following controls to it:

<div>
    <p>
        <strong>Categories:</strong>
        <asp:Repeater ID="CategoriesRepeater" runat="server">
            <HeaderTemplate><ul></HeaderTemplate>
            <ItemTemplate>
                <li><%# Container.DataItem %></li>
            </ItemTemplate>
            <FooterTemplate></ul></FooterTemplate>
        </asp:Repeater>
    </p>
</div>
 
<div>
    <p>
        <strong>Tags:</strong>
        <asp:Repeater ID="TagsRepeater" runat="server">
            <HeaderTemplate><ul></HeaderTemplate>
            <ItemTemplate
                <li><%# Container.DataItem %></li>
            </ItemTemplate>
            <FooterTemplate></ul></FooterTemplate>
        </asp:Repeater>
    </p>
</div>

Add the following properties to your Widget’s code-behind file:

/// <summary>
/// guid array from designer for our selected tags
/// </summary>
public Guid[] selectedTags;
 
/// <summary>
/// Gets or sets the selected tags.
/// </summary>
public Guid[] SelectedTags
{
    get
    {
        if (selectedTags == null) selectedTags = new Guid[] { };
        return selectedTags;
    }
    set { selectedTags = value; }
}
 
/// <summary>
/// guid array from designer for our selected categories
/// </summary>
public Guid[] selectedCategories;
 
/// <summary>
/// Gets or sets the selected categories.
/// </summary>
/// <value>
/// The selected categories.
/// </value>
public Guid[] SelectedCategories
{
    get
    {
        if (selectedCategories == null) selectedCategories = new Guid[] { };
        return selectedCategories;
    }
    set { selectedCategories = value; }
}
 
/// <summary>
/// Intermediary property for passing Tags to and from the designer
/// </summary>
/// <value>
/// The tag value as a comma delimited string.
/// </value>
public string TagValue
{
    get { return string.Join(",", SelectedTags); }
    set
    {
        var list = new List<Guid>();
        if (value != null)
        {
            var guids = value.Split(',');
            foreach (var guid in guids)
            {
                Guid newGuid;
                if (Guid.TryParse(guid, out newGuid))
                    list.Add(newGuid);
            }
        }
        SelectedTags = list.ToArray();
    }
}
 
/// <summary>
/// Intermediary property for passing categories to and from the designer
/// </summary>
/// <value>
/// The category value as a comma-delimited string.
/// </value>
public string CategoryValue
{
    get { return string.Join(",", SelectedCategories); }
    set
    {
        var list = new List<Guid>();
        if (value != null)
        {
            var guids = value.Split(',');
            foreach (var guid in guids)
            {
                Guid newGuid;
                if (Guid.TryParse(guid, out newGuid))
                    list.Add(newGuid);
            }
        }
        SelectedCategories = list.ToArray();
    }
}

Add the following Control References to your Widget’s code-behind file:

/// <summary>
/// Reference to the Label control that shows the Message.
/// </summary>
protected virtual Repeater TagsRepeater
{
    get
    {
        return this.Container.GetControl<Repeater>("TagsRepeater", true);
    }
}
 
/// <summary>
/// Reference to the Label control that shows the Message.
/// </summary>
protected virtual Repeater CategoriesRepeater
{
    get
    {
        return this.Container.GetControl<Repeater>("CategoriesRepeater", true);
    }
}

Add the following method calls to your Widget’s code-behind InitializeControls method:

            BindTags();
            BindCategories();

Add the following two methods to your Widget’s code-behind:

/// <summary>
/// Binds the selected tags to your tags control
/// </summary>
private void BindTags()
{
    // retrieve selected tags
    var tags = new List<string>();
    foreach (var tagID in SelectedTags)
    {
        // get tag name
        var tag = taxMgr.GetTaxon(tagID);
        if (tag == null) continue;
        tags.Add(tag.Name);
    }
 
    // bind tag names
    TagsRepeater.DataSource = tags;
    TagsRepeater.DataBind();
}
 
/// <summary>
/// Binds the selected tags to your categories control
/// </summary>
private void BindCategories()
{
    // retrieve selected categories
    var tags = new List<string>();
    foreach (var tagID in SelectedCategories)
    {
        // get category name
        var tag = taxMgr.GetTaxon(tagID);
        if (tag == null) continue;
        tags.Add(tag.Name);
    }
 
    // bind category names
    CategoriesRepeater.DataSource = tags;
    CategoriesRepeater.DataBind();
}

 

Adding Categories and Tags to your Designer

This is the first time we will be touching the Designer code as Thunder took care of that for us previously. It’s a little different, because it has different responsibilities, and has an associated javascript file, but in the end it’s just a custom Widget.

Open your custom Designer’s UI file and add the following controls to it:

<%-- Tag section --%>
 <li>
     <sitefinity:FlatTaxonField
         ID="TagsSelector"
         runat="server"
         DisplayMode="Write"
         WebServiceUrl="~/Sitefinity/Services/Taxonomies/FlatTaxon.svc/cb0f3a19-a211-48a7-88ec-77495c0f5374"
         TaxonomyMetafieldName="Tags"
         AllowMultipleSelection="true"
         Expanded="false"
         Title="Tags" />
 </li>
 
 <li class="sfFormCtrl">
     <asp:HiddenField ID="TagValue" runat="server" />
 </li>
 
 <%-- Category section --%>
 <li>
     <label class="sfTxtLbl">Category:</label>
     <sitefinity:HierarchicalTaxonField
         ID="CategoriesSelector"
         runat="server"
         DisplayMode="Write"
         Expanded="false"
         ExpandText="ClickToAddCategories"
         ShowDoneSelectingButton="true"
         AllowMultipleSelection="true"
         BindOnServer="false"
         TaxonomyMetafieldName="Category"
         WebServiceUrl="~/Sitefinity/Services/Taxonomies/HierarchicalTaxon.svc" />
 </li>
  
 <li class="sfFormCtrl">
     <asp:HiddenField ID="CategoryValue" runat="server" />
 </li>

Make sure you have the following Using statements to your Designer’s code-behind file:


Now add the following Control References to the Designer’s code-behind:

/// <summary>
 /// Gets the control that is bound to the TagValue property
 /// </summary>
 protected virtual Control TagValue
 {
     get
     {
         return this.Container.GetControl<Control>("TagValue", true);
     }
 }
 
 /// <summary>
 /// Gets the tag selector control
 /// </summary>
 protected FlatTaxonField TagsSelector
 {
     get { return Container.GetControl<FlatTaxonField>("TagsSelector", true); }
 }
 
 /// <summary>
 /// Gets the control that is bound to the CategoryValue property
 /// </summary>
 protected virtual Control CategoryValue
 {
     get
     {
         return this.Container.GetControl<Control>("CategoryValue", true);
     }
 }
 
 /// <summary>
 /// Gets the category selector control
 /// </summary>
 protected HierarchicalTaxonField CategoriesSelector
 {
     get { return Container.GetControl<HierarchicalTaxonField>("CategoriesSelector", true); }
 }

Now add two method calls to your Designer’s code-behind InitializeControls method:

// initialize the taxonomy selectors
CategoriesSelector.TaxonomyId = TaxonomyManager.CategoriesTaxonomyId;
TagsSelector.TaxonomyId = TaxonomyManager.TagsTaxonomyId;

Now add the following component and element property descriptors within the IScriptControl implementation Region:

descriptor.AddElementProperty("tagValue", this.TagValue.ClientID);
descriptor.AddComponentProperty("TagsSelector", this.TagsSelector.ClientID);
descriptor.AddElementProperty("categoryValue", this.CategoryValue.ClientID);
descriptor.AddComponentProperty("CategoriesSelector", this.CategoriesSelector.ClientID);

That’s it for the Designer UI and code-behind file. Now we’ll start editing the JavaScript file.

Add the following JavaScript variables:

this._TagsSelector = null;
this._tagValue = null;
this._CategoriesSelector = null;
this._categoryValue = null;

 

Add the following items to the bottom of the refreshUI function:

/* RefreshUI TagValue */
jQuery(this.get_tagValue()).val(controlData.TagValue);
 
/* RefreshUI CategoryValue */
jQuery(this.get_categoryValue()).val(controlData.CategoryValue);
 
// load tags
var t = this.get_TagsSelector();
var tags = controlData.TagValue;
if (tags != null)
    t.set_value(controlData.TagValue.split(","));
 
// load categories
var c = this.get_CategoriesSelector();
var cats = controlData.CategoryValue;
if (cats != null)
    c.set_value(controlData.CategoryValue.split(","));

Now add the following items to the bottom of the applyChanges function:

/* ApplyChanges SelectedPageID */
/* ApplyChanges TagValue */
controlData.TagValue = jQuery(this.get_tagValue()).val();
 
/* ApplyChanges CategoryValue */
controlData.CategoryValue = jQuery(this.get_categoryValue()).val();
 
// save selected tags
var t = this.get_TagsSelector();
var tags = t.get_value();
if (tags != null)
    controlData.TagValue = t.get_value().join();
 
// save selected categories
var c = this.get_CategoriesSelector();
var cats = c.get_value();
if (cats != null)
    controlData.CategoryValue = c.get_value().join();

Now add the following properties to the existing Properties section:

/* TagValue properties */
get_tagValue: function () { return this._tagValue; },
set_tagValue: function (value) { this._tagValue = value; },
 
/* CategoryValue properties */
get_categoryValue: function () { return this._categoryValue; },
set_categoryValue: function (value) { this._categoryValue = value; },
 
// Categories Selector
get_CategoriesSelector: function () { return this._CategoriesSelector; },
set_CategoriesSelector: function (value) { this._CategoriesSelector = value; },
 
// Tags Selector
get_TagsSelector: function () { return this._TagsSelector; },
set_TagsSelector: function (value) { this._TagsSelector = value; }

Compile your project one final time and use to your heart’s content!

 

Summary

Hopefully everyone found this a useful exercise and this sheds more insight into how Designers are made and what’s going on under the hood. Once you see the patterns at play here you will no doubt be able to apply your own custom controls and functionality to your Sitefinity Designers.

Though Designers aren’t necessary in all instances when creating custom widgets, they can be a very powerful tool for your business users, because, once setup, they allow the business user to customize their content display properties without the need of a developer. And as we all know a happy business user makes for a happy developer!

 

Sample Code

Please go to my Github Repository to download the sample project.

47 comments

Leave a comment
  1. Steve Apr 17, 2014

    Hmmm...saving\loading the categories and tags is too complex

    //Load
    JSON.parse(tagValue);

    //Save
    JSON.stringify(tagValue);

    ...and use ServiceStack.Text on the front end to convert it back to a Guid[] list...and one of my extensions to "GetTags" or "GetCategories"
    http://goo.gl/bW2TDn

    Also unsure why we need to have hidden fields for the values?...totally unnecessary.

  2. Johan May 07, 2014
    The example on Tags and Categories is not working for me. Anyone else have the same problem with the example?
  3. David May 07, 2014

    Hi Johan,

    I lifted the code from this blog by Josh Morales: 

    http://www.sitefinity.com/blogs/teamblog/posts/team-blog/2011/10/05/selecting_sitefinity_4_content_inside_widget_designers

    I also have a link in the Sample Code section to a github repository where you can download the source code to take a look.

    I would suggest taking a look at these first if you haven't already. If you have, then maybe you can share what SF version you are using and what error you are receiving? I didn't have any issues with it myself, but I might have missed pointing out something in my post that I take for granted working with SF all the time.  

    Cheers,

    David

  4. Johan May 08, 2014

    David,

    I am using Sitefinity 6.3. The detailed error is posted here: http://bit.ly/1kP4e6c

  5. David May 09, 2014

    Hi Johan,

    Can you upload your code to a github repository or somewhere publicly available where I can grab the files?  All I need is your mvc files and your files. 

    Cheers,

    David

  6. David May 09, 2014
    Oops... Need your mvc and designer files.
  7. Johan May 12, 2014
    I changed the access modifier for the SelectedTags properties to private and it works properly for me. Not quite sure if my explanation is right but the detailed explanation is here http://bit.ly/1kP4e6c
  8. Jason Johnson Sep 18, 2014

    Hi,

    I noticed there's an AllowExternalPagesSelection attribute on the PagesSelector control. I want to allow my users to alternatively enter an external URL. However, when I enable this option it doesn't seem to save the URL. Is there example code showing how to allow for both sitefinity page selection and external page links?

    Thanks,
    Jason

  9. Davud Sep 26, 2014

    Hi Jason,

    I don't know offhand, but I'll take a look and see if I can find that info for you.

  10. Vijay Jan 15, 2016
    Thank You !! Worked for me :)
  11. asd Apr 12, 2018
  12. robert Apr 13, 2018
    I appreciate everything you have added to my knowledge base.Admiring the time and effort you put into your blog and detailed information you offer.Thanks.devpost.com/terristewart8
  13. robert Apr 13, 2018
    Thanks for taking the time to discuss this, I feel strongly about it and love learning more on this topic.attorneys los angeles
  14. robert Apr 13, 2018
    I can see that you are an expert at your field! I am launching a website soon, and your information will be very useful for me.. Thanks for all your help and wishing you all the success in your business.apartment bookkeeper
  15. robert Apr 13, 2018
    I am very happy to discover your post as it will become on top in my collection of favorite blogs to visit.best cruises for teens
  16. robert Apr 13, 2018
    thanks for the tips and information..i really appreciate it..children dentist torrington ct
  17. robert Apr 13, 2018
    Positive site, where did u come up with the information on this posting?I have read a few of the articles on your website now, and I really like your style. Thanks a million and please keep up the effective work.Living trust attorney fees
  18. robert Apr 13, 2018
    I recently came across your blog and have been reading along. I thought I would leave my first comment. I don't know what to say except that I have enjoyed reading. Nice blog. I will keep visiting this blog very often.Best Coffee shop
  19. robert Apr 13, 2018
    New web site is looking good. Thanks for the great effort.authority
  20. robert Apr 13, 2018
    Wow what a Great Information about World Day its very nice informative post. thanks for the post.law school prep
  21. robert Apr 13, 2018
    Thanks for sharing the info, keep up the good work going.... I really enjoyed exploring your site. good resource...cleaning services
  22. robert Apr 13, 2018
    This one is good. keep up the good work!..Thanks for sharing this useful info..extractions
  23. robert Apr 13, 2018
    Wonderful illustrated information. I thank you about that. No doubt it will be very useful for my future projects. Would like to see some other posts on the same subject! kitchen cabinets online
  24. robert Apr 13, 2018
    Thanks for the blog post buddy! Keep them coming...Thanks, that was a really cool read!top cryptocurrency list
  25. robert Apr 13, 2018
    Great post, and great website. Thanks for the information!bathtub refinishing near me
  26. robert Apr 13, 2018
    This is my first time i visit here and I found so many interesting stuff in your blog especially it's discussion, thank you.printing new york
  27. robert Apr 13, 2018
    Great article Lot's of information to Read...Great Man Keep Posting and update to People..Thankspatient experience surveys
  28. robert Apr 13, 2018
    Love to read it,Waiting For More new Update and I Already Read your Recent Post its Great Thanks.Small business loan NY
  29. robert Apr 13, 2018
    Awesome and interesting article. Great things you've always shared with us. Thanks. Just continue composing this kind of post. Finance Karma
  30. robert Apr 13, 2018
    It is a great website.. The Design looks very good.. Keep working like that!. Van Signage Shropshire
  31. robert Apr 13, 2018
    This is really a nice and informative, containing all information and also has a great impact on the new technology. Thanks for sharing it,Computer Repairs
  32. devpost.com/terristewart8 Apr 14, 2018
    Hey what a brilliant post I have come across and believe me I have been searching out for this similar kind of post for past a week and hardly came across this. Thank you very much and will look for more postings from you.devpost.com/terristewart8
  33. robo Apr 18, 2018
    I have read your blog it is very helpful for me. I want to say thanks to you. I have bookmark your site for future updates.DETECTIVES LOGROÑO
  34. robo Apr 25, 2018
    This is really a nice and informative, containing all information and also has a great impact on the new technology. Thanks for sharing itDETECTIVES BARCELONA
  35. robo May 01, 2018
    thanks for the tips and information..i really appreciate it..DETECTIVES PRIVADOS MADRID
  36. sami May 08, 2018
    It proved to be Very helpful to me and I am sure to all the commentators here!TSM Agency
  37. sohail khatri May 08, 2018
    Thanks for sharing this quality information with us. I really enjoyed reading. Will surely going to share this URL with my friends. Test Bank for Leading and Managing in Canadian Nursing 1st Edition by Yoder Wise
  38. sami May 17, 2018
    Great info! I recently came across your blog and have been reading along. I thought I would leave my first comment. I don’t know what to say except that I have.DETECTIVE MADRID
  39. sami May 31, 2018
    I am unable to read articles online very often, but I’m glad I did today. This is very well written and your points are well-expressed. Please, don’t ever stop writing.Detectives Madrid
  40. sohail khatri Jun 04, 2018
    Free download HD full length Latest Bollywood Movies, New Hindi Movie, Webmusic, Bengali songs, Punjabi, Old Hindi Songs, Telugu Songs, Tamil Dubbed Movies Webmusic
  41. robo Jun 07, 2018
    Wow i can say that this is another great article as expected of this blog.Bookmarked this site..Detectives España
  42. sami Jun 23, 2018
    This is the type of information I’ve long been trying to find. Thank you for writing this information.Detective en Madrid
  43. jone Jun 26, 2018
    I read your blog frequently and I just thought I’d say keep up the amazing work!flowers Scottsdale
  44. jone Jul 04, 2018
    Please let me know if you’re looking for a article writer for your site. You have some really great posts and I feel I would be a good asset. If you ever want to take some of the load off, I’d absolutely love to write some material for your blog in exchange for a link back to mine. Please send me an email if interested. Thank you!DETECTIVE MADRID
  45. robert Jul 16, 2018
    I just found this blog and have high hopes for it to continue. Keep up the great work, its hard to find good ones. I have added to my favorites. Thank You.Prescott Electricians
  46. robert Jul 16, 2018
    This was a really great contest and hopefully I can attend the next one. It was alot of fun and I really enjoyed myself..tin roofing
  47. detective Jul 17, 2018
    Nice to be visiting your blog again, it has been months for me. Well this article that i’ve been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article. Thanks, great share.detectives privados Madrid

    Leave a comment