The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.
This blog post can serve as a successor of the one Rado wrote a year ago on the same subject. However, there have been many improvements in the Module Builder and Sitefinity Thunder since then that will allow us to accomplish the task much easier. I hope it can be seen as a complete guide of how to work with related dynamic content items.
For the purpose of this blog, I have created a new content type called Apps. As starter, it includes just a few fields: Title, Description and ImageGallery
Figure: initial definition of the App module.
The module is active and working just fine. Now let’s add some additional functionality to it – ability to select related items (related Apps). The goal is to easily create relationships between the items (apps) so that any app can have zero, one or more related apps. That’s a common requirement, right? So let’s get started.
First, we need to create the interface that will allow us to *select* the related apps. Thankfully, Sitefinity Thunder can generate all the code for us. Just go to your solution in Visual Studio, right click the SitefinityWebApp project and select Add > New Item. In the left pane, under your favourite language (C# or VB) select the Sitefinity category. Choose “Sitefinity Dynamic Items Field Control Selector” from the right.
Figure: adding a new dynamic items field control selector to your project.
Let’s call this file RelatedAppsControlSelector. Click add. If you’ve already installed Thunder to this project it will find all the existing dynamic modules and you can select the one you need. We select the App content type in this case:
Figure: Sitefinity Thunder successfully identified all the active dynamic module types in my project.
Sitefinity Thunder will then create a whole bunch of files for us:
Figure: Sitefinity Thunder generates all the files needed for the selector to work properly
Those files will take care of the interface for selecting related items and persisting the data in the database. Rebuild the project in Visual Studio so that the logic in those files is included in the output assembly.
We need a field in our App module that will store information about the related apps. The one to do the job is of type Array of GUIDs (as we want to support multiple related items). Next we need to select what interface we are going to use to select the related apps. Here we enter the type of the class we added in step 1.
Figure: Adding the RelatedApps field to the dynamic content type.
Note the value of the “Type or Virtual pat of the custom widget” field – it should contain the full path to the class that was created in step 1. Since I placed it into the ~/custom/fields/ folder I have SitefinityWebApp.Custom.Fields.RelatedAppsControlSelector in the type name.
Continue then Save and finally check the Update Widget Template option:
Figure: update the widget template of the Apps so that it reflects the changes in the module structure
Now let’s go back to Content > Apps and add some items. You’ll see the Add Items button which opens up a modal window with a list of the existing apps. Very consistent with the rest of the UI in the backend. And all this without writing a single line of code!!
Figure: adding related items in dynamic content types is easy as cake
Now, let’s look how those apps look in the frontend:
Figure: The out-of-the-box widget template is unable to show the related apps
Not the result we are expecting, right? The reason is because the RelatedApp field stores GUID numbers of the related apps so we need a way to find the real apps behind the GUIDs and show the relevant information. To do so, we are going to override the default Full Details widget template and provide our own implementation of it.
Create a new Web User Control in Visual Studio – I called it FullAppDetails.ascx and placed it under the ~/custom/templates folder.
Paste the following code inside of it:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="FullAppDetails.ascx.cs" Inherits="SitefinityWebApp.Custom.Templates.FullAppDetails" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.DynamicModules.Web.UI.Frontend" TagPrefix="sf" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.Fields" TagPrefix="sf" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sf" %>
<
sf:DynamicDetailContainer
id
=
"detailContainer"
runat
=
"server"
>
<
LayoutTemplate
>
<
div
class
=
"sfitemDetails"
>
<
sf:SitefinityLabel
ID
=
"mainShortTextFieldLiteral"
runat
=
"server"
Text='<%# Eval("Title") %>' WrapperTagName="h1" HideIfNoText="true" CssClass="sfitemTitle" />
<
sf:FieldListView
ID
=
"PublicationDate"
runat
=
"server"
Format
=
"{PublicationDate.ToLocal():MMM d, yyyy, HH:mm tt}"
WrapperTagName
=
"div"
WrapperTagCssClass
=
"sfitemPublicationDate"
/>
<
sf:SitefinityLabel
ID
=
"SitefinityLabel1"
runat
=
"server"
Text='<%# Eval("Description")%>' WrapperTagName="div" HideIfNoText="true" CssClass="sfitemLongText" />
<
sf:ImageAssetsField
ID
=
"ImageAssetsField1"
runat
=
"server"
DataFieldName
=
"AppImageGallery"
IsThumbnail
=
"False"
/>
<
sf:AssetsField
ID
=
"AssetsField1"
runat
=
"server"
DataFieldName
=
"AppVideoGallery"
/>
<
telerik:RadListView
ID
=
"relatedAppsListView"
runat
=
"server"
ItemPlaceholderID
=
"ItemsContainer"
EnableEmbeddedSkins
=
"false"
OnNeedDataSource
=
"relatedAppsListView_NeedDataSource"
EnableEmbeddedBaseStylesheet
=
"false"
>
<
EmptyDataTemplate
>
<%--<
div
>No Related Apps</
div
>--%>
</
EmptyDataTemplate
>
<
LayoutTemplate
>
RELATED APPS:
<
ul
class
=
"sflistList"
>
<
asp:PlaceHolder
ID
=
"ItemsContainer"
runat
=
"server"
/>
</
ul
>
</
LayoutTemplate
>
<
ItemTemplate
>
<
li
>
<
h3
>
<
a
href='<%# Eval("UrlName") %>' ><%# Eval("Title") %></
a
>
</
h3
>
</
li
>
</
ItemTemplate
>
</
telerik:RadListView
>
</
div
>
</
LayoutTemplate
>
</
sf:DynamicDetailContainer
>
<
asp:PlaceHolder
ID
=
"socialOptionsContainer"
runat
=
"server"
></
asp:PlaceHolder
>
Note the RadListView control we’ve added – it will show the related apps in more appropriate way. We just need to bind the list view with data – this is done in the NeedDataSource event:
protected
void
relatedAppsListView_NeedDataSource(
object
sender, Telerik.Web.UI.RadListViewNeedDataSourceEventArgs e)
{
RadListView relatedAppsListView = sender
as
RadListView;
DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager();
Type appType = TypeResolutionService.ResolveType(
"Telerik.Sitefinity.DynamicTypes.Model.Apps.App"
);
DynamicContent app = ((DetailItem)relatedAppsListView.Parent).DataItem
as
DynamicContent;
//the array of GUIDs of the related apps
Guid[] relatedAppsIDs = app.GetValue(
"RelatedApps"
)
as
Guid[];
//a list that will hold the related apps
List<DynamicContent> relatedAppsCollection =
new
List<DynamicContent>();
for
(
int
i = 0; i < relatedAppsIDs.Length; i++)
{
//get the related app
DynamicContent relatedApp = dynamicModuleManager.GetDataItem(appType, relatedAppsIDs[i]);
//add it to the list
relatedAppsCollection.Add(relatedApp);
}
relatedAppsListView.DataSource = relatedAppsCollection;
}
Build the project and go to the backend. We should tell the widget to use this particular template in the front end. To do so, edit the page where the widget is placed and edit the widget itself. Go to Advanced > ControlDefinition > Views > DynamicContentDetailView and find the TemplatePath field. Enter the relative full path to the user control created above, e.g. ~/Custom/Templates/FullAppDetails.ascx and Save.
Now go to the details of any of the apps and you’ll see something like this:
View all posts from Veselin Vasilev on the Progress blog. Connect with us about all things application development and deployment, data integration and digital business.
Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.
Learn MoreSubscribe to get all the news, info and tutorials you need to build better business apps and sites
Progress collects the Personal Information set out in our Privacy Policy and the Supplemental Privacy notice for residents of California and other US States and uses it for the purposes stated in that policy.
You can also ask us not to share your Personal Information to third parties here: Do Not Sell or Share My Info
We see that you have already chosen to receive marketing materials from us. If you wish to change this at any time you may do so by clicking here.
Thank you for your continued interest in Progress. Based on either your previous activity on our websites or our ongoing relationship, we will keep you updated on our products, solutions, services, company news and events. If you decide that you want to be removed from our mailing lists at any time, you can change your contact preferences by clicking here.