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

How to display an image gallery for each news item

by Slavo Ingilizov

An image gallery for a single news item

We’ve seen many requests for the option to associate a Sitefinity news item with a collection of images. A long time ago I blogged on how you could associate a single news item with a single image. Extending that sample to enable multiple image selection is not very hard, and this blog post is going to show you how to do it.

If you just want the source code for the sample, you can download it. Follow the installation instructions at the end of the blog post.

A selector for a single image

The sample you can read about in the previous blog post implements a single image selector. This is a summary of what has been implemented so far:

  • A selector control, which displays a list of images and returns the ID of the one clicked by the user
  • A dialog which wraps the selector control and returns the ID of the image when closed
  • A field control with two modes:
    • Write mode (used in the backend to select the image) – opens the dialog and saves the returned image ID.
    • Read mode (used to display the selected image in the news widget template) – reads the image ID, and outputs an <img> tag with correct URL.

NOTE: This blog post is an extension to the previous one. It will only go over the new functionality and changes, so if you haven’t gone through the initial implementation, I recommend you do that now.

What needs to be changed

To support multiple images, we need to change the following from our previous sample:

  • Modify the selector to allow multiple selection
  • Modify the field control to display all images in Read Mode.

We will go over each of the above items and see how it was implemented. The way the dialog returns the selected images, and how the field control persists them in the custom field stays the same. Instead of saving the ID, we will save a comma-separated list of IDs.

Modify the selector to allow multiple selection

The image selector should be able to highlight multiple selected images. In the previous sample, any time you click an image, your previous selection was lost (because you could only select one). This is all done in the Javascript _binderCommand method of the SimpleImageSelector. Here is the changed code that handles multiple image selection:

if (args.get_commandName() == "selectImage") {
    var imageUrl = args.get_dataItem().ThumbnailUrl;
    var imageId = args.get_dataItem().Id;
 
    var jItemElement = jQuery(args.get_itemElement())
    var isItemAlreadySelected = jItemElement.attr('class').indexOf('sf_selectedImage') != -1;
     
    if (isItemAlreadySelected) {
        var itemIndex = this.arrayContains(this._selectedImageIds, imageId);
        if (itemIndex !== -1) {
            this._selectedImageIds.splice(itemIndex, 1);
        }
 
        var itemIndexUrl = this.arrayContains(this._selectedImageUrls, imageUrl);
        if (itemIndex !== -1) {
            this._selectedImageUrls.splice(itemIndex, 1);
        }
        jItemElement.removeClass("sf_selectedImage");
    }
    else {
        if (this.arrayContains(this._selectedImageIds, imageId) === -1) {
            this._selectedImageIds.push(imageId);
        }
        if (this.arrayContains(this._selectedImageUrls, imageUrl) === -1) {
            this._selectedImageUrls.push(imageUrl);
        }
        jItemElement.addClass("sf_selectedImage");
    }
}

The ID of the image that was clicked is passed as an argument. We keep the IDs and URLs of the already selected images in two arrays. These arrays were not present in the previous sample, where we only kept a single ID and URL. We check if the item is already added, and add or remove it accordingly. Each new item the user clicks will be added to the array, or removed on second click. The code for changing the style of the selected image and returning the selected value does not change.

Modify the field control to display all images in Read Mode

Once a collection of images is selected, the value is persisted as a comma-separated string of their IDs. We need to parse that string and get the images themselves using the API. In the previous version, with only one image to select, the Value property of the field control was returning the ID directly from the UI. We now need to add a private field to hold the value. Here is the full implementation of the Value property. 

public override object Value
{
    get
    {
        var val = string.Empty;
        switch (this.DisplayMode)
        {
            case FieldDisplayMode.Read:
                if (this.value != null)
                    val = this.value.ToString();
                break;
            case FieldDisplayMode.Write:
                if (this.value != null)
                    val = this.value.ToString();
                break;
        }
        return val;
    }
    set
    {
        if (this.ChildControlsCreated)
        {
            switch (this.DisplayMode)
            {
                case FieldDisplayMode.Write:
                    this.TextBoxControl.Text = value as string;
                    break;
 
                case FieldDisplayMode.Read:
                    var imageIds = value.ToString().Split(';');
                    this.value = imageIds;
                    break;
            }
            this.value = null;
        }
        else
        {
            this.value = value;
        }
    }
}

Apart from doing the selection itself, our field control also has the job of presenting the selected images when used in Read mode (i.e. the widget template of a widget). For the purposes of this sample, we will just bind a Repeater control to the list of images. This is done in three of the methods – OnLoad, InitializeControls, and OnPreRender.

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    if (this.DisplayMode == FieldDisplayMode.Read)
        this.ImageGalleryControl.ItemDataBound += new RepeaterItemEventHandler(ImageGalleryControl_ItemDataBound);
}
 
protected override void InitializeControls(GenericContainer container)
{
    this.ConstructControl();
    if (this.DisplayMode == FieldDisplayMode.Read)
    {
        this.LabelControl.Visible = false;
    }
}
 
protected override void OnPreRender(EventArgs e)
{
    base.OnPreRender(e);
    if (this.DisplayMode == FieldDisplayMode.Read)
    {
        var filterBuilder = new StringBuilder();
        var imageIds = this.Value.ToString().Split(',');
        for (var i = 0; i < imageIds.Length; i++)
        {
            if (i > 0)
            {
                filterBuilder.Append(" OR ");
            }
            filterBuilder.Append(String.Format("Id == {0}", imageIds[i]));
        }
        var filter = filterBuilder.ToString();
 
        var images = App.WorkWith().Images().Where(i => i.Status == ContentLifecycleStatus.Master).Where(filter).Get();
        this.ImageGalleryControl.DataSource = images;
        this.ImageGalleryControl.DataBind();
    }
}
 
void ImageGalleryControl_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
    {
        var imageControl = e.Item.FindControl("image") as System.Web.UI.WebControls.Image;
        if (imageControl != null)
        {
            imageControl.ImageUrl = (e.Item.DataItem as Telerik.Sitefinity.Libraries.Model.Image).ThumbnailUrl;
        }
    }
}

By using the Split() method, we convert the comma-separated string of IDs into an array. From those IDs, we build a filter, which can be passed to the API to get a collection of the selected images. The we just set the Repeater’s data source and call its DataBind() method. On the ItemDataBound event of the Repeater, we set the URL of each <img> tag to the selected image’s thumbnail.

Including the field control in a widget template

Once we’ve implemented multiple selection in our field control, we can include it in the widget template for each news item. This is done with the following markup:

<%@ Register Assembly="ThumbnailSelectorField" Namespace="Telerik.Sitefinity.Samples" TagPrefix="samples" %>
...
<samples:SimpleImageField ID="imgGalleryField" runat="server" Value='<%# Eval("Thumbnails") %>'></samples:SimpleImageField>

Note that in the Value property, we need to supply the name of the custom field which we use to persist the selected images. The next section lists all steps you need to follow in order to setup the multiple image selector for the News module.

Installation Instructions

  1. Create an empty Sitefinity project
  2. Download the source code for the sample
  3. Include the Global.asax and Global.asax.cs files in your web project
  4. Include the existing ThumbnailSelectorField project from the archive, and change its references to point to the BIN folder of your web project. Also include a project reference to it in the web project
  5. Build and run your project
  6. Create a custom field in News with name “Thumbnails” and select a Custom widget for entering data. Enter “Telerik.Sitefinity.Samples.SimpleImageSelector” for the widget type.
  7. Edit the widget template for the Full news item mode of news.
  8. Register the “samples” prefix with the following line:
    <%@ Register Assembly="ThumbnailSelectorField" Namespace="Telerik.Sitefinity.Samples" TagPrefix="samples" %>
  9. Include the field control in <ItemTemplate> section
    <samples:SimpleImageField ID="imgGalleryField" runat="server" Value='<%# Eval("Thumbnails") %>'></samples:SimpleImageField>

If you have further questions about the sample, please ask them in the comments.

9 comments

Leave a comment
  1. Bostjan May 29, 2012
    I'm getting runtime error:
    The control of type 'Telerik.Sitefinity.Samples.SimpleImageSelector' does not implement IField interface. All fields must implement IField interface.
  2. Slavo May 29, 2012
    Hey Bostjan.

    Sorry about this. The correct type in step 6 should be "Telerik.Sitefinity.Samples.SimpleImageField". This should fix the error.
  3. Bostjan May 30, 2012
    At Runtime in Read mode in file SimpleImageField.cs method
    void ImageGalleryControl_ItemDataBound(object sender, RepeaterItemEventArgs e)
    doesn't really get called at runtime even though there are images 
    this.ImageGalleryControl.ItemDataBound += new RepeaterItemEventHandler(ImageGalleryControl_ItemDataBound);
    is executed at OnLoad. DataSource has items! Any thoughts?
  4. Slavo May 30, 2012
    Are you using the field in a widget template, like described in the blog post? Initially I had the same problem, but at the time I was subscribing to the event in InitializeControls, rather than OnLoad. When moved to OnLoad, everything was fine.

    Does the datasource have items when you debug, inside the field? If that's the case, then you can try and move the subscription to the event earlier in the control lifecycle, rather than OnLoad.
  5. Umar Ashraf Jan 08, 2013

    what does the following  sentence means and how do we do it. when you say "Add reference to the web project" Does it mean to the sitefinity web project or the empty web project we created in point 1.


    4.   Include the existing ThumbnailSelectorField project from the archive, and change its references to point to the BIN folder of your web project. Also include a project reference to it in the web project

  6. Slavo Jan 08, 2013
    Hello Umar,

    Those two are actually the same. The empty project you created IS a Sitefinity web project. There are no other web projects.

    Slavo.
  7. Umar Jan 09, 2013
    I get the following error when i try adding the Custom field to the news

    Type "Telerik.Sitefinity.Samples.SimpleImageFieldElement, SitefinityWebApp" cannot be resolved.


    Any ideas ?


    Thanks
    Umar
  8. Slavo Jan 09, 2013

    Sitefinity cannot find the type of the custom field. It could be either because there is not reference to the assembly which contains it, or because the namespace is not the same. Please make sure you have referenced the class library project from the web project and all names are the same.

  9. Shelly Dec 05, 2013
    When I try to implement this, I get a javascript error "Unable to get property 'value' of undefined or null reference" at Telerik.Web.UI.WebResource.axd, line 11142 character 40 which is "calculateCharactersCount:function(){if(this.get_textElement().value==undefined)." How do I fix this?

    Leave a comment