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
;
<%@ 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"
/>
<%@ Control Language=
"C#"
Inherits=
"SitefinityWebApp.WidgetTemplates.BlogPostList.BlogPostList"
%>
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
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
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...
Hello,
Steve is right on track with this and this is the best method to add additional logic to default controls.
Regards,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.
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);
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.
No problem mate. Glad it worked out :)
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");
//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 = "";
afaik "Image" isn't a native field in a SF blog post...? At least has never been there in my instances
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...
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?
Can you paste in what you have?
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...
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.
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.