Howto Guide: Add Code behind to a Sitefinity Widget Template - Front- & Back-End Development - Front- & Back-End Development - Progress Community
 Front- & Back-End Development

Howto Guide: Add Code behind to a Sitefinity Widget Template

  • Howto Guide: Add Code behind to a Sitefinity Widget Template
  • This is something my colleague figured out and is going to be invaluable to us in all our developments.  The ability to use Sitefinity built-in "Widget Template" functionality, and then setting the codebehind file to use.

    NB: Not sure if this is "Sitefinity Approved" but it certainly works, and seems a great deal better than any other solution I've tried (including the OpenAccessDataProvider,1283719324jkhdskf12384kjh4.ascx method - people who have used this will know what I'm talking about!!)

    1) Add in your Visual Studio solution, a Folder - name doesn't matter, e.g., "WidgetTemplates", then (if you want) a subfolder to keep things organised, e.g., "BlogPostList"

    2) Inside this subfolder, create a blank .CS file that is going to be called by your Widget Template.  My example calls it "BlogPostList.cs".  This needs to be the same structure as if it was code-behind to a Web User Control.  Here below is my whole code ATM that adds a custom ItemDataBound handler, to display images in my Blog Post List widget template.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Sitefinity.Web.UI;
    using Telerik.Web.UI;
    using Telerik.Sitefinity;
    using Telerik.Sitefinity.Blogs.Model;
    using Telerik.Sitefinity.Model;
     
    namespace SitefinityWebApp.WidgetTemplates.BlogPostList
        public partial class BlogPostList : System.Web.UI.UserControl
            protected override void OnInit(EventArgs e)
                base.OnInit(e);
     
                var repeater = (RadListView)this.FindControl("Repeater");
                repeater.ItemDataBound += new EventHandler<RadListViewItemEventArgs>(repeater_ItemDataBound);
            
     
            void repeater_ItemDataBound(object sender, RadListViewItemEventArgs e)
                if (e.Item is RadListViewDataItem)
                    var args = (RadListViewDataItem)e.Item;
                    var blogPost = (BlogPost)args.DataItem;
                    var imagePath = DataExtensions.GetValue<String>(blogPost, "Image");
                    var imageCtl = (Image)e.Item.FindControl("imgBlogImage");
     
                    if (!string.IsNullOrEmpty(imagePath) && imageCtl != null)
                        imageCtl.ImageUrl = imagePath;
                     else if (imageCtl != null)
                        imageCtl.Visible = false;
                    
                
            
        

    3) In the Backend, go to Administration -> Widget Templates -> The Template you want to interact with.  (My example will use a very slightly customised Widget Template for Blog Post lists - close enough to Default to not matter, but you can use this method on any Widget Template).  Below is the Widget Template code:
    <%@ Control Language="C#" Inherits="SitefinityWebApp.WidgetTemplates.BlogPostList.BlogPostList" %>
    <%@ 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" Assembly="Telerik.Sitefinity" %>
    <%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit" Assembly="Telerik.Sitefinity" %>
    <%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %>
    <%@ Import Namespace="Telerik.Sitefinity" %>
     
    <telerik:RadListView ID="Repeater" ItemPlaceholderID="ItemsContainer" runat="server" EnableEmbeddedSkins="false" EnableEmbeddedBaseStylesheet="false">
      <LayoutTemplate>
        <sf:ContentBrowseAndEditToolbar ID="MainBrowseAndEditToolbar" runat="server" Mode="Add"></sf:ContentBrowseAndEditToolbar>
        <ul class="sfpostsList sfpostListTitleDateContent">
          <asp:PlaceHolder ID="ItemsContainer" runat="server" />
        </ul>
      </LayoutTemplate>
     
      <ItemTemplate>
        <li class="sfpostListItem">
          <h2 class="sfpostTitle">
            <sf:DetailsViewHyperLink TextDataField="Title" ToolTipDataField="Description" runat="server" />
          </h2>
               
          <div class="sfpostAuthorAndDate">
            <asp:Literal ID="Literal2" Text="<%$ Resources:Labels, By %>" runat="server" />
            <sf:PersonProfileView runat="server" />
            <asp:Literal ID="Literal3" runat="server" Text=" on " />
            <sf:FieldListView ID="PostDate" runat="server" Format=" PublicationDate.ToLocal():dd MMM, yyyy" />
            <sf:CommentsBox ID="itemCommentsLink" runat="server" CssClass="sfpostCommentsCount"/>
          </div>
           
          <div class="imgWrap">
            <asp:Image id="imgBlogImage" runat="server" />
          </div>
           
          <sf:FieldListView ID="PostContent" runat="server" Text="0" Properties="Content" WrapperTagName="div" WrapperTagCssClass="sfpostContent" />
          <sf:DetailsViewHyperLink Text="Continue reading this blog post" runat="server" />
          <sf:ContentBrowseAndEditToolbar ID="BrowseAndEditToolbar" runat="server" Mode="Edit,Delete,Unpublish"></sf:ContentBrowseAndEditToolbar>
        </li>
      </ItemTemplate>
    </telerik:RadListView>
    <sf:Pager id="pager" runat="server"></sf:Pager>
    <asp:PlaceHolder ID="socialOptionsContainer" runat="server" />

    4) Add at the top of the Widget Template, the reference to Inherits:
    <%@ Control Language="C#" Inherits="SitefinityWebApp.WidgetTemplates.BlogPostList.BlogPostList" %>

    That's it!  My codebehind now has bound a custom event handler to the RadListView and sets the ImageUrl by getting the data from a Custom Field in the Backend.  This is just a tiny taste that could easily be done in other ways, but this ability to use server side code with a Widget Template like this is so powerful...

    Immediate examples that I'm going to extend:
    1) Creating a dynamically generated Summary.  Instead of having to set the "Summary" field in the Backend, I plan to get the whole Blog Post article, shorten it to ~100 characters, add "..." at the end - Instant Summary!

    2) Instead of just using ImageUrl, I need to implement AltText also.  I'll lookup the image (by Url at this stage, but hopefully by Guid) and grab the AltText from the Image object.

    3) Customising the "Read more" <sf:DetailsViewHyperLink Text="Continue reading this blog post" runat="server" />.  Instead of "Continue reading this blog" I want it to say "Continue reading 'Title'

    I'm sure there's hundreds of smarter things to do with this as well, it's very exciting.  Would love any feedback, especially from Sitefinity devs as to why this is a good or bad approach?

  • This is great. Thanks for sharing! 
    Looking forward to the feedback from Sitefinity!
  • This is a fantastic find, and deserves a bump....way more elegant than any of the other solutions! :) *cough*SfCtrlPresentation folder*cough*
  • Thanks for sharing you get another bump.  :)
  • Dear Stephen

    Great. I was anoyed by the search results summery. I might now simply change it to diplay the description of the page. 

    I don't know what it means to give a bump. But sure would +1 or like it on social medias :-)

    Markus

    PS: Since the widget templates are in my opinion rather developer orientated how about getting codebehind files out of the box?
  • Thanks @Steve.

    I'm not that clever, my colleagues invented this - I just posted it and take the credit!!  Hence I'm not exactly sure how this all hangs together, but I concur with the end result:

    * You get the default Sitefinity code runs first
    * Any code you add using this method runs second and is the "master"

    That's how I think of it anyway...
  • Hi Stephen,

    Thanks for that, exactly what I'm looking for. Would this affect any of the other code associated with the widget already? Just wondering if for example the Blog post's comment function would stop working because you're essentially binding the widget template to a different class.

    I don't have a great understanding of WebForms templates, which might be why I'm asking the question. Sometimes it seems to me that templating allows you to associate two objects with a control?

    Regards,
    Jacques
  • @J.Hoventer
      Any widget using that template would then run the associated codebehind yeah...however keep in mind you aren't binding it to another class, but almost like adding a "layer".

    The control is still SimpleView based, and all the code it runs, it will continue to run.  This usercontrol workaround lets you hook into the page events and reference controls on the page.

    The simpleview is the root control, which references a "View" which is the ascx...this is almost like giving the ascx it's own seperate codebehind (if that makes sense)

    Steve
  • Hello,

     Steve is right on track with this and this is the best method to add additional logic to default controls.

    Regards,
    Patrick Dunn
    the Telerik team
    Do you want to have your say in the Sitefinity development roadmap? Do you want to know when a feature you requested is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  • Has anyone done something like this with widget templates based off dynamic modules?

    Right now the only way I can get code behind working for a dynamic widget template is by mapping it like described in this blog post.

    http://www.sitefinity.com/blogs/jen-pelevas-blog/2013/03/31/how-to-create-lightbox-gallery-in-a-custom-module
  • Absolutely!  All the time.. But not for a while, we're more into using custom EVAL functions at this time.

    What seems to be the trouble?
  • I will be away from the project for a couple days so I can't get the exact error right now, but it was something about a template missing a dynamicDetails container ID error.

    template could not find a dynamic details container with ID something...   Not very helpful I know, but I will update this when I am back at the computer with the actual project.
  • Oh, cool... Sounds like you may have changed the ID of the RadListView?

    If I drop a default widget on, in List mode, but change:
    <telerik:RadListView ID="dynamicContentListView"
    to
    <telerik:RadListView ID="dynamicContentListView2"

    I get this error:
    A required control was not found in the template for "~/SfCtrlPresentation/OpenAccessDataProvider,b693ab6499f94fb49add92195f062685.ascx". The control must be assignable from type "Telerik.Web.UI.RadListView" and must have ID "dynamicContentListView".

    Suspect your problem is something similar.  Lots of the stuff in widget templates has to be left default, e.g., Sitefinity breaks when:
    * Renaming or removing RadListView
    * Removing <sf:Pager>
    etc...

    Hope that helps.

  • Thanks Stephen and Patrick,

    Your recommendations lead me to the real issue, which in the end is pretty much the same situation.  I am smacking myself for not catching this....one of those days where I was over complicating what I was trying to do.

    So the first problem is that I was trying to use a RadListView on a details view template.  So the error was in fact complaining that it was missing an assignable type for the DynamicDetailContainer with ID "detailContainer".  

    I went back into my widget template and added <sf:DynamicDetailContainer ID="detailContainer" runat="server"> which took care of my problem.


    Thank you both for the help.