Howto Guide: Add Code behind to a Sitefinity Widget Template

Posted by Community Admin on 04-Aug-2018 22:50

Howto Guide: Add Code behind to a Sitefinity Widget Template

All Replies

Posted by Community Admin on 04-Jun-2012 00:00

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?

Posted by Community Admin on 15-Jun-2012 00:00

This is great. Thanks for sharing! 
Looking forward to the feedback from Sitefinity!

Posted by Community Admin on 24-Jul-2012 00:00

This is a fantastic find, and deserves a bump....way more elegant than any of the other solutions! :) *cough*SfCtrlPresentation folder*cough*

Posted by Community Admin on 05-Sep-2012 00:00

Thanks for sharing you get another bump.  :)

Posted by Community Admin on 05-Sep-2012 00:00

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?

Posted by Community Admin on 04-Apr-2013 00:00

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

Posted by Community Admin on 04-Apr-2013 00:00

@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

Posted by Community Admin on 04-Apr-2013 00:00

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

Posted by Community Admin on 09-Apr-2013 00:00

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

Posted by Community Admin on 16-Apr-2013 00:00

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

Posted by Community Admin on 16-Apr-2013 00:00

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?

Posted by Community Admin on 16-Apr-2013 00:00

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.

Posted by Community Admin on 16-Apr-2013 00:00

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.

Posted by Community Admin on 18-Apr-2013 00:00

Hi,

 Stephen is correct.

This method inherits from the existing widget so the requirements outlined by it are still in place. It is a custom user control where the necessary components are dictated by code. For example like this:

public HyperLink SeeAllContentLink
        
            get
            
                return this.Container.GetControl<HyperLink>("SeeAllContentLink", false);
            
        

for each necessary control on the template. Overriding does not remove these relationships so you must keep those controls on the template with their original ID but you can add additional controls. All the best,
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

Posted by Community Admin on 18-Apr-2013 00:00

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.  

Posted by Community Admin on 18-Apr-2013 00:00

No problem mate.  Glad it worked out :)

Posted by Community Admin on 10-Jun-2013 00:00

So it appears that Sitefinity 6 no longer has an Image in a BlogPost, and I'm getting an error on :

var imagePath = DataExtensions.GetValue<String>(blogPost, "Image");

So to get this working I replaced with :

//var imagePath = DataExtensions.GetValue<String>(blogPost, "Image");
                var imagePath = DataExtensions.GetValue<Lstring>(blogPost, "Content").ToString();
 
                if (imagePath.Contains("<img "))
                
 
                    imagePath = imagePath.Substring(imagePath.IndexOf("<img "));
 
                    imagePath = imagePath.Substring(imagePath.IndexOf("src=\"") + 5);
 
                    imagePath = imagePath.Substring(0, imagePath.IndexOf("\""));
 
                
                else
                
                    imagePath = "";
                

To get the image path out of the content.  Can anyone confirm that Image has been removed from BlogPost in Sitefinity 6, or Does someone have a better way of getting the image out of the blog?

Posted by Community Admin on 10-Jun-2013 00:00

afaik "Image" isn't a native field in a SF blog post...?  At least has never been there in my instances

Posted by Community Admin on 10-Jun-2013 00:00

Steve is correct, Image is not and has not ever been a native field for Blogs.  We use the ImageSelectorField that we took from a blog post or sample somewhere...

Posted by Community Admin on 20-Jan-2016 00:00

So I got this working for ProductDetail page.

The problem that I am having is that I need to manipulate properties of the product for the page I am viewing.  So far no matter what I am trying I am unable to get to the product that I am currently viewing.

Can anyone point me to what I am missing?

Posted by Community Admin on 20-Jan-2016 00:00

Can you paste in what you have? 

Posted by Community Admin on 02-Jun-2016 00:00

Is this how to still valid and working in version 9. I have tried to implement it and it breaks the widget template. I dont think i have made a mistake...

Posted by Community Admin on 02-Jun-2016 00:00

It's amazing how far Sitefinity has come since this post was written so long ago...  Project Feather is the best thing ever in Sitefinity Dev, since Dynamic Modules.

 

I'm sorry to give an answer that is almost certainly not helpful to you in your current situation - you should 100% learn Project Feather and do whatever you're trying to do now using it.

 

However, if you post your WidgetTemplate & Code behind class we can see if anything looks awry.  Can't see why this wouldn't work in 9+ but as you can imagine, haven't done this method for a while now.

Posted by Community Admin on 02-Jun-2016 00:00

Missed your question, sorry.

 

In the end I linked into the data binding event that was available on the page.  At that point I was able to hook into the product object and therefore I was able to access the data needed.  

 

Thanks.

This thread is closed