Best practices for implementing custom widgets

Overview

When you develop custom widgets, you need to solve problems such as how to implement canonical URLs, what are the best development practices, or how to reuse built-in widgets logic. This article demonstrates how to use important interfaces or abstract base classes to solve these problems.

ContentBaseController

You use ContentBaseController abstract class to construct all the out-of-the-box SEO properties for the widget in Detail view mode - when displaying a single item. When you implement a descendant of this class, you can call the InitializeMetadataDetailsViewBag method and pass the item that is currently being displayed. Based on the item and the SEO configurations of the site, Sitefinity CMS automatically creates the Open Graph and other SEO properties.
GITHUB EXAMPLE: For more information about this class, see the sample code in Sitefinity CMS GitHub repository » ContentBaseController.cs.

ContentModelBase

The ContentModelBase class holds the auxiliary implementation for various functionality, such as getting content locations, creating generic list view models, creating generic details view models, creating a list view model based on a related data item, part of the implementation of the IHasCacheDependency interface, and more.
For more information, see ContentModelBase.cs.

IContentLocatableView

You implement the IContentLocatableView interface, if you want your custom widget to have a canonical URL and to provide a location for items.
For more information, see Register content location with your custom widgets and How to implement IContentLocatableView for custom widgets.

IHasCacheDependency

You implement IHasCacheDependency interface to provide cache invalidation for the respective content type in your custom widget. If this interface is not implemented, there can be stale content displayed in the following scenario:
  • The user that is logged in is not a backend user or no user is logged in.
    Sitefinity CMS does not use cache when serving content to backend users.
  • The widget is displayed on a page and the user has requested this page, thus, the query made by the widget is cached according to the cache profile.
  • An editor makes a change to an item that the widget is displaying.
  • The user will not see the change until the cache expires, since there is no way for Sitefinity CMS to know that these items are being displayed by this widget.
For more information, see Implement Cache Dependencies.

IRouteMapper

You implement IRouteMapper interface when you need custom logic for resolving the route parameters. When you implement it, the default behavior for MVC routing will be turned off and only your custom routing implementation will be used.

EXAMPLE: You have custom book widget that displays a list of books on a page. You can implement custom routing logic for filtering by a specific author that will resolve the required author from the route – for example, http://mysite/book-catalog/Index/Author. For more information, see #region IRouteMapper in the following sample:


HandleUnknownAction

You use the HandleUnknownAction method in a Sitefinity CMS widgets to retain a correct state in certain scenarios. One such scenario is the details mode of a separate widget. When a page is rendered with a widget in a mode different than the default one, Sitefinity CMS checks all the widgets on the page whether they can go into this mode. In this case, the news widget is going into details mode and Sitefinity CMS is trying to push all widgets in this mode. The widgets who have details action will get mapped earlier and Sitefinity CMS will not execute them. However, the widgets that do not have details action must implement the HandleUnknownAction method so that the developer can specify what action should be executed in such a case.
For more information see HandleUnknownAction in the following sample:

Returning a view

When you return a view in your Controller class, always name explicitly the view that is going to be called:
return this.View("ViewName", viewModel);
For more information, see the following sample:

Was this article helpful?