+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

Creating a Simple Image Selector Using a Client Binder

by Slavo Ingilizov

Introduction

There have been numerous requests by the Sitefinity community asking for a custom control used to select images for a particular scenario. This and the next few blog posts are going to show how you can create a simple image selector, and how you can plug it into Sitefinity to select thumbnails for news items. You can download the source code from here.

Web Services and Client Binders

As you know, Sitefinity has an extensive web service API, which you can use to create, display, update or delete all kinds of content managed by the CMS. We’re going to use these web services in our example to get a list of images and display them to the user.

Since writing the client-side code to invoke a web service and work with its response can be quite cumbersome, we’ve included a set of built-in controls, called Client Binders, that make your job much easier. A client binder is essentially a custom control used to call a web service and display the result in another custom control). We have implemented binders for most RadControls used in Sitefinity that you can use out of the box – RadGridBinder, RadTreeViewBinder, RadListViewBinder, GenericCollectionBinder. You can find more information about client binders here.

In our example, we are going to use a GenericCollectionBinder working with the Sitefinity image service, since the markup we’re outputting will be custom and we’re not using RadGrid or RadTreeView. Here’s how the end result is going to look like:

A simple image selector

The role of this control is to display a list of images available in Sitefinity, and return the URL of the image that the user clicked. We can later use this URL in whatever way we find appropriate (we’re going to cover that in the next blog post).

Code Walkthrough

In the template for our image selector, we have two controls – a GenericCollectionBinder, and an HTML server control representing an unordered list of images: 

<ul id="imageList" runat="server">
</ul>
 
<sitefinity:GenericCollectionBinder ID="imageListBinder" runat="server"
        TargetId="imageList"
        ServiceUrl="~/Sitefinity/Services/Content/ImageService.svc/"
        BindOnLoad="false"
        DataKeyNames="Id"
        DataMembers="Id, Title, ThumbnailUrl">
        <Containers>
            <sitefinity:BinderContainer ID="imageTitleContainer" runat="server"RenderContainer="true">
                <div style="float:left; margin: 10px 10px 10px 10px; padding: 10px">
                    <a href="javascript:void(0);" class="sf_binderCommand_selectImage">
                        <img sys:src="{{ThumbnailUrl}}" />
                    </a>
                    <li>{{Title}}</li>
                </div>
            </sitefinity:BinderContainer>
        </Containers>
</sitefinity:GenericCollectionBinder>

There are several properties we need to set on the binder - the URL of the service we are using, the ID of the target control displaying the result, the name of the data item property serving as a key, and a list of the properties we are using. As you can see, we also have a BinderContainer. You can think of it as the item template, or markup that will be rendered for each item returned by the service. In our case, we render a thumbnail, and the name of each image. The things in the curly brackets are actually placeholders. When binding, the binder will replace them with the corresponding value of the given properties. This acts like the Eval() function in ASP.NET, but performs its magic on the client.

Server Control

The SimpleImageSelector class inherits from SimpleScriptView. This is a widely used Sitefinity class which providers the basis of all script controls (using both server and client functionality). It also provides a container, through which we can access all controls we place in the template. It is recommended that you inherit from it when writing your custom controls. Our server-side code for the image selector does 3 things:

  1. Specify the template we are using (by overriding the LayoutTemplatePath property):
    public override string LayoutTemplatePath
    {
        get
        {
            return "~/ImageSelector/SimpleImageSelector.ascx";
        }
        set
        {
            base.LayoutTemplatePath = value;
        }
    }
  2. Include a custom stylesheet (to signify the selected images after the user clicks them):
    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);
        HtmlLink link = new HtmlLink();
        link.Attributes.Add("href", VirtualPathUtility.ToAppRelative("~/ImageSelector/SimpleImageSelector.css"));
        link.Attributes.Add("type""text/css");
        link.Attributes.Add("rel""stylesheet");
        this.Page.Header.Controls.Add(link);
    }
  3. Provide a reference to the corresponding client-side component (overriding GetScriptReferences and GetScriptResources).
There is no other logic on the server, and everything our control does is implemented on the client.

Client Control

We're building a very simple image selector, so there's only two things it does:

  1. Tell the binder to invoke the service and display the result (make sure you have uploaded at least one image).     
    _onLoad: function (sender, args) {
        this.get_binder().DataBind();
    },
  2. After a user clicks a particular image, mark it and save its URL
    _binderCommand: function (sender, args) {
        if (args.get_commandName() == "selectImage") {
            var imageUrl = args.get_dataItem().MediaUrl;
            this.set_selectedImageUrl(imageUrl);
            // remove class from previously selected images
            var selected = jQuery(args.get_itemElement().parentNode).find("div.sf_selectedImage").each(function (index, element) {
                jQuery(element).removeClass("sf_selectedImage");
            });
            // set class to currently selected image
            jQuery(args.get_itemElement()).addClass("sf_selectedImage");
        }
    },

A particularly interesting feature we've used in this case are binder commands. We can instruct all client binders to fire a command with a particular name, by specifying a special class in our markup. In our case we have the following on the <a> tag:

<a href="javascript:void(0);" class="sf_binderCommand_selectImage">
    <img sys:src="{{ThumbnailUrl}}" />
</a>

When the binder sees this, it knows that when the <a> tag is clicked, it has to fire a command with the name "selectImage". It is also kind enough to pass us all the information for the particular item that we need. We then handle the command, and update our selected URL. With this, the role of our simple image selector is done.

Installation Instructions

  1. Create a new website, or use and existing one, and open its .csproj file in Visual Studio (should open as a web application project, not a website).
  2. Download the source code for this example.
  3. Extract the ZIP in the main folder of your site.
  4. Include the folder in your project (If the folder is not visible in visual studio, click the "show all files" button in Solution Explorer).
  5. Right click the SimpleImageSelector.js file, click Properties, set BuildAction to "EmbeddedResource".
  6. Open AssemblyInfo.cs file and add the following line at the bottom:
    [assembly: WebResource("SitefinityWebApp.ImageSelector.SimpleImageSelector.js""application/x-javascript")]
  7. Run your project
  8. Log into the backend, and from Administration -> Settings -> Advanced, add your new control to the toolbox.
  9. Create a new page, put the control on the page and see how it works.

What's Next

In the next blog post, we're going to see how we can use the image selector, to choose Thumbnails for news items in the Sitefinity backend.

4 comments

Leave a comment
  1. Clint H. Jul 09, 2013
    What would be the best way to replicate the Sitefinity 3.7 SP4 code:

    <sf:ButtonSelectorWindowNavigateUrl="~/Sitefinity/UserControls/Dialogs/ImageEditorDialog.aspx"AssociatedControls="hidBioImage,imgBio"ItemTemplatePath="~/Sitefinity/Admin/ControlTemplates/Libraries/Dialogs/ButtonSelector.ascx"runat="server"cssclass="pickerWrapper"ButtonText="Select Image"/>

    In Sitefinity 5.4?
  2. Shriyal Aug 19, 2016

    In the related video to deploy, also in the followup blog, you mentioned :Telerik.Sitefinity.Samples.SimpleImageField as type of the custom widget. Where does "SimpleImageField" come from? The sources got file: SimpleImageSelector.ascx.

    Have you built the control using these sources and steps?

  3. Shriyal Aug 19, 2016
    Just as expected : Type "Telerik.Sitefinity.Samples.SimpleImageField" cannot be resolved.
  4. Shriyal Aug 19, 2016

    Edit: I am using version 9 of Sitefinity.

    Telerik.Sitefinity.Samples.SimpleImageSelector works in custom field. Then you save and exit out of site. REBUILD the project as it changes config files.

    And now when you go to news, create or edit: you get -

    The control of type 'Telerik.Sitefinity.Samples.SimpleImageSelector' does not implement IField interface. All fields must implement IField interface.

    Leave a comment