Filter Custom Image Selector by Album? - Front- & Back-End Development - Front- & Back-End Development - Progress Community
 Front- & Back-End Development

Filter Custom Image Selector by Album?

  • Filter Custom Image Selector by Album?
  • Hey Telerik folks (specifically Ivan),

    Really enjoyed this post and am happily moving along with images in both blogs and news.  I was wondering, though, if there was an easy method of filtering those images shown by album.  In other words, I have tens of images in the system and don't really want or need to see them when picking an image for blog or news posts.  All I really need is to be able to see images from a particular album in the picker.

    Anyway, any easy method for implementing that? 

    Thanks

    - William

  • I'm assuming, perhaps, this is a FilterExpression applied to the GenericCollectionBinder that specifies an album filter on the image side.  Just don't know what the filter should look like.

    Thanks!
  • I got it:

    FilterExpression="Visible = true AND Status = Live AND Album.Id=6DB14A9E-880C-4C8C-9FAF-06D7D71EC6FC"

    Powerful stuff, though it seems a bit fragile.  Is there a better way to handle this filtering?

    Thanks.

    - William
  • Hi William,

    If you use the property from the designer, this is the only way to do it. We are going to create a topic about how to use this property FilterExpression, because currently it is not officially documented and there are only samples on the forums.

    Regards,
    Ivan Dimitrov
    the Telerik team
  • Thanks, Ivan.

    For now, that approach works for me.  Looking forward to the continuation of your blog posts, though. 

    Great stuff!

    - William
  • hey guys,

    I went through the Custom Image selector and added the option to filter by album. I default to showing the selected image and a drop down of all albums. Choosing an album hides the selected image and shows all images from that list. i plan to upload it to the marketplace for everyone to get. it currently only supports one instance on the form at a time But i'll give you the basic idea to enable the Image selector to allow filtering by an album. It should be all prettied up and submitted to the marketplace sometime next week, but I hate waiting around indefinitely on stuff so.. here you go

    Please note that I moved everything to the ImageSelector folder and adjusted the namespaces accordingly

    (part of file) SimpleImageField.ascx OnClientShow and OnClientClose were added to the rad window and and a class called txtForImageField was added to textbox.
    <telerik:RadWindowManager ID="windowManager" runat="server" Skin="Sitefinity">
           <Windows>
              <telerik:RadWindow ID="simpleImageSelector" Width="600" Height="400" NavigateUrl="~/Sitefinity/Dialog/SimpleImageSelectorDialog" runat="server" ReloadOnShow="true"
                             Modal="true" VisibleStatusbar="false" Behaviors="Close"
                             OnClientShow="OnImageSelectorClientShow" OnClientClose="OnImageSelectorClientClose" >
                            </telerik:RadWindow>
                        </Windows>
                    </telerik:RadWindowManager>
     
                    <asp:TextBox ID="textBox" runat="server" CssClass="sfTxt txtForImageField" />



    (part of file) SimpleImageField.js  (just appended to end of file)
    $ = jQuery;
    var _previousItem = null;
     
    function OnImageSelectorClientShow(sender, args)
        if ($(".txtForImageField").length > 0 && $(".txtForImageField").val().length > 0)
            _previousItem = $(".txtForImageField").val();
        
        if (_previousItem != null)
            sender.argument = _previousItem;
        
    function OnImageSelectorClientClose(sender, args)
        _previousItem = null;

    (whole file) SimpleImageSelector.ascx added a GenericCollectionBinder a dropdown for albums, and a preview image I defaulted the filterexpression on images to a filter that would show no images and added OnClientDataBound Methods
    <%@ Control Language="C#" AutoEventWireup="true" %>
     
    <%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sitefinity" %>
     
    <sitefinity:ResourceLinks ID="resourcesLinks" runat="server">
        <sitefinity:ResourceFile JavaScriptLibrary="JQuery">
        </sitefinity:ResourceFile>
    </sitefinity:ResourceLinks>
     
    <select name="albumList" id="albumList" class="albumList" runat="server">
    </select><img alt="loading albums.." class="loadingAlbums" src="/images/loading.gif" />
     
    <ul id="imageList" class="imageList" runat="server">
    </ul><img alt="loading images.." class="loadingImages" style="display:none;" src="/images/loading.gif" />
     
    <sitefinity:GenericCollectionBinder ID="albumListBinder" runat="server"
            TargetId="albumList"
            ServiceUrl="~/Sitefinity/Services/Content/AlbumService.svc/"       
            BindOnLoad="false"        
            DataKeyNames="Id"
            DataMembers="Id, Title" OnClientDataBound="OnAlbumsDataBound">
            <Containers>           
                <sitefinity:BinderContainer ID="selectOptionContainer"  runat="server" TemplateHolderTag="Select" RenderContainer="false">
                  <option sys:value="Id">Title</option>
                </sitefinity:BinderContainer>
            </Containers>
        </sitefinity:GenericCollectionBinder>
     
    <sitefinity:GenericCollectionBinder ID="imageListBinder" runat="server"
            TargetId="imageList"
            ServiceUrl="~/Sitefinity/Services/Content/ImageService.svc/"
            FilterExpression="Parent.Id == 00000000-0000-0000-0000-000000000000"
            OnClientDataBound = "OnImagesDataBound"
            BindOnLoad="false"        
            DataKeyNames="Id"
            DataMembers="Id, Title, ThumbnailUrl" CssClass="imageListBinder">
            <Containers>           
                <sitefinity:BinderContainer ID="imageTitleContainer" runat="server" RenderContainer="false">
                    <li><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>
                        <div>Title</div>
                        </div>
                    </li>               
                </sitefinity:BinderContainer>
            </Containers>
    </sitefinity:GenericCollectionBinder>
    <div class="previewImage" style="display:none;">
        <h3>Current Image</h3>
        <img alt="preview" src="" />
    </div>

    (whole file) SimpleImageselector.js added the albumBinder and appended some jQuery to the end of the file
    Type.registerNamespace("SitefinityWebApp.ImageSelector");
     
    SitefinityWebApp.ImageSelector.SimpleImageSelector = function (element)
        SitefinityWebApp.ImageSelector.SimpleImageSelector.initializeBase(this, [element]);
     
        this._binder = null;
        this._binderAlbum = null;
        this._onLoadDelegate = null;
        this._onUnloadDelegate = null;
        this._binderCommandDelegate = null;
        this._binderAlbumCommandDelegate = null;
        this._selectedImageUrl = null;
     
    SitefinityWebApp.ImageSelector.SimpleImageSelector.prototype =
        initialize: function ()
            SitefinityWebApp.ImageSelector.SimpleImageSelector.callBaseMethod(this, "initialize");
     
            this._onLoadDelegate = Function.createDelegate(this, this._onLoad);
            Sys.Application.add_load(this._onLoadDelegate);
            this._onUnloadDelegate = Function.createDelegate(this, this._onUnload);
            Sys.Application.add_unload(this._onUnloadDelegate);
            this._binderCommandDelegate = Function.createDelegate(this, this._binderCommand);
            this._binderAlbumCommandDelegate = Function.createDelegate(this, this._binderAlbumCommand);
        ,
     
        dispose: function ()
            SitefinityWebApp.ImageSelector.SimpleImageSelector.callBaseMethod(this, "dispose");
     
            Sys.Application.remove_load(this._onLoadDelegate);
            if (this._onLoadDelegate)
                delete this._onLoadDelegate;
            
            Sys.Application.remove_load(this._onUnloadDelegate);
            if (this._onUnloadDelegate)
                delete this._onUnloadDelegate;
            
        ,
     
        /* -------------------- public methods ------------ */
     
        /* -------------------- events -------------------- */
     
        /* -------------------- event handlers ------------ */
     
        _onLoad: function (sender, args)
            this.get_binder().add_onItemCommand(this._binderCommandDelegate);
            this.get_binderAlbum().add_onItemCommand(this._binderAlbumCommandDelegate);
            this.get_binder().DataBind();
            this.get_binderAlbum().DataBind();
        ,
     
        _onUnload: function (sender, args)
            this.get_binder().remove_onItemCommand(this._binderCommandDelegate);
            this.get_binderAlbum().remove_onItemCommand(this._binderAlbumCommandDelegate);
        ,
     
        _binderCommand: function (sender, args)
            if (args.get_commandName() == "selectImage")
                var imageUrl = args.get_dataItem().ThumbnailUrl;
                this.set_selectedImageUrl(imageUrl);
                $(".previewImage img").attr("src", imageUrl);
                // remove class from previously selected images
                var selected = jQuery(args.get_itemElement().parentNode).find("li.sf_selectedImage").each(function (index, element)
                    jQuery(element).removeClass("sf_selectedImage");
                );
                // set class to currently selected image
                jQuery(args.get_itemElement()).addClass("sf_selectedImage");
            
        ,
         _binderAlbumCommand: function (sender, args)
     
        ,
     
        /* -------------------- private methods ----------- */
     
        /* -------------------- properties ---------------- */
     
        get_binder: function ()
            return this._binder;
        ,
     
        set_binder: function (value)
            this._binder = value;
        ,
         
        get_binderAlbum: function ()
            return this._binderAlbum;
        ,
     
        set_binderAlbum: function (value)
            this._binderAlbum = value;
        ,
     
        get_selectedImageUrl: function ()
            return this._selectedImageUrl;
        ,
     
        set_selectedImageUrl: function (value)
            this._selectedImageUrl = value;
        
    ;
     
     
    SitefinityWebApp.ImageSelector.SimpleImageSelector.registerClass("SitefinityWebApp.ImageSelector.SimpleImageSelector", Sys.UI.Control);
     
     
    $ = jQuery;
    var _theImageBinder = null;
    var _previousItem = null;
     
    function OnAlbumsDataBound(sender, commandArgs)
        $(".albumList").prepend("<option value=''>Choose an Album</value>")
        $(".albumList").val("");
        $(".loadingAlbums").hide();
     
    function OnImagesDataBound(sender, commandArgs)
        _theImageBinder = sender;
        $(".loadingImages").hide();
     
    function GetRadWindow()
        var oWindow = null;
        if (window.radWindow)
            oWindow = window.radWindow;
        else if (window.frameElement.radWindow)
            oWindow = window.frameElement.radWindow;
        return oWindow;
     
    var _initialLoad = true;
    function pageLoad()
        if (_initialLoad)
            var currentWindow = GetRadWindow();
            if (currentWindow.argument != null && currentWindow.argument.length > 0)
                $(".previewImage img").attr("src", currentWindow.argument);
                $(".previewImage").show();
             else
                $(".previewImage").hide();
            
             
            _initialLoad = false;
        
     
    $(document).ready(function ()
     
        $(".albumList").change(function ()
            if ($(this).val() != "")
                $(".previewImage").hide();
                $(".loadingImages").show();
                if (_theImageBinder != null)
                    _theImageBinder.set_filterExpression("Parent.Id == " + $(".albumList").val());
                    _theImageBinder.DataBind();
                    $(".imageList").show();
                 else
                    alert('Binder not found');
                
             else
                $(".imageList").hide();
                $(".previewImage").show();
            
        );
    );

    (part of file)SimpleImageSelector.cs  Added the backend logic for the AlbumBinder

        //dg this property is new
        /// <summary>
        /// Gets a reference to the RadListViewBinder control in the template
        /// </summary>      
        protected virtual GenericCollectionBinder BinderAlbum
        
            get
            
                return this.Container.GetControl<GenericCollectionBinder>("albumListBinder", true);
            
        
    public override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        
            var descriptors = new List<ScriptDescriptor>();
            var descriptor = new ScriptControlDescriptor(typeof(SimpleImageSelector).FullName, this.ClientID);
            //dg Added
            descriptor.AddComponentProperty("binderAlbum", this.BinderAlbum.ClientID);
            descriptor.AddComponentProperty("binder", this.Binder.ClientID);
     
            descriptors.Add(descriptor);
            return descriptors.ToArray();
        
  • Hi Drew Greenwell,

    Thanks a lot for sharing this with the community. We may highlight it in a blog post once you've uploaded it to the marketplace. Please update this thread when you do.

    @William Apart from the Filter expression, another way is to change the service URL of the binder, depending on the album. There's a method in the ImageService to get child items. The URL to invoke it is:

    ~/Sitefinity/Services/Content/ImageService.svc/parent/albumId

    Just replace albumId with the real Guid. You can use the set_serviceBaseUrl() method of the binder for this.

    Regards,
    Slavo
    the Telerik team
  • I keep getting the following error when using the GenericCollectionBinder to populate the album list:
    'System.Web.UI.HtmlControls.HtmlSelect' does not allow child controls.

    I have also looked at the following page, http://www.sitefinity.com/40/help/developers-guide/deep-dive-client-side-programming-client-binder-controls-generic-collection-binder.html, which also uses a select element to be bound to.

    If I change the select element to a div element and run the control, it will render option elements as child elements of the div element. No other elements are present so I don't understand the error.

    The site is running on version 4.0.1210. Any thoughts??

    Edit::

    Stack Trace if it helps
    [HttpException (0x80004005): 'System.Web.UI.HtmlControls.HtmlSelect' does not allow child controls.]
       System.Web.UI.EmptyControlCollection.ThrowNotSupportedException() +108
       System.Web.UI.EmptyControlCollection.Add(Control child) +4
       Telerik.Sitefinity.Web.UI.Templates.RootBuilder.CreateChildControls(Control parent, Control bindingContainer, PlaceHoldersCollection placeHolders) +579
       Telerik.Sitefinity.Web.UI.Templates.ObjectBuilder.CreateObject(Control bindingContainer, PlaceHoldersCollection placeHolders) +766
       Telerik.Sitefinity.Web.UI.Templates.ControlBuilder.CreateControl(Control bindingContainer, PlaceHoldersCollection placeHolders) +18
       Telerik.Sitefinity.Web.UI.Templates.ControlBuilder.CreateControl(Control bindingContainer) +12
       Telerik.Sitefinity.Web.UI.Templates.RootBuilder.CreateChildControls(Control parent, Control bindingContainer) +269
       Telerik.Sitefinity.Web.UI.StringTemplate.InstantiateIn(Control container, PlaceHoldersCollection placeHolders) +88
       Telerik.Sitefinity.Web.UI.StringTemplate.InstantiateIn(Control container) +7
       Telerik.Sitefinity.Web.UI.SimpleView.CreateContainer(ITemplate template) +35
       Telerik.Sitefinity.Web.UI.SimpleView.get_Container() +33
       Telerik.Sitefinity.Web.UI.SimpleView.CreateChildControls() +46
       System.Web.UI.Control.EnsureChildControls() +102
       System.Web.UI.Control.PreRenderRecursiveInternal() +42
       System.Web.UI.Control.PreRenderRecursiveInternal() +175
       System.Web.UI.Control.PreRenderRecursiveInternal() +175
       System.Web.UI.Control.PreRenderRecursiveInternal() +175
       System.Web.UI.Control.PreRenderRecursiveInternal() +175
       System.Web.UI.Control.PreRenderRecursiveInternal() +175
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2496

  • Hello Richard Baugh,

    Can you ensure that you have runat="server" on the select tag? That's the only thing from the top of my head, I'm not sure why you are getting this error. Please send your whole control (and template) and I'll try to debug it and help more.

    Regards,
    Slavo
    the Telerik team
    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get now >>
  • Yes, it has runat="server" as an attribute. I will open a support ticket to upload the project.