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

Selecting documents in Field Controls with EditorContentManagerDialog

by Svetoslav Petsov

There have been a lot of requests for a sample on Field Controls with a document selector, so I decided to show you how to make use of Sitefinity's EditorContentManagerDialog to select content inside field controls. This sample is simplified as much as it could be - there are no custom selectors, no hosting of dialogues inside windows - just the built-in EditorContentManagerDialog inside a field control. 
The sample is built, using Slavo's ThumbnailSelectorField (for simplicity, the names inside have not been changed, so that it can be easier for you to make the connection between the two controls), so I won't be explaining in detail how field controls work, as this is already done by Slavo in his blog post
In his sample, Slavo had a field control, an image selector and a dialogue. What I did was remove the selector and the dialogue and use only the field control. Inside, I put an EditorContentManagerDialog, which already has selectors inside, and even has different modes for selecting files/images, so it would be easy for you to transform this into an Image selector, as well.
Here's how I modified the template of the field control:

<%@ Control Language="C#" AutoEventWireup="true" %>
 
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sf" %>
<%@ Register Assembly="ThumbnailSelectorField" Namespace="Telerik.Sitefinity.Samples" TagPrefix="samples" %>
 
<sf:ResourceLinks ID="resourcesLinks" runat="server">
    <sf:ResourceFile JavaScriptLibrary="JQuery">
    </sf:ResourceFile>
</sf:ResourceLinks>
 
<sf:ConditionalTemplateContainer ID="conditionalTemplate" runat="server">
    <Templates>
        <sf:ConditionalTemplate Left="DisplayMode" Operator="Equal" Right="Read" runat="server">       
            <sf:SitefinityLabel id="titleLabel_read" runat="server" WrapperTagName="div" HideIfNoText="false" CssClass="sfTxtLbl"></sf:SitefinityLabel>
            <sf:SitefinityLabel id="textLabel_read" runat="server" WrapperTagName="div" HideIfNoText="false" CssClass="sfTxtContent"></sf:SitefinityLabel>
            <sf:SitefinityHyperLink id="documentLink" runat="server" />
            <sf:SitefinityLabel id="descriptionLabel_read" runat="server" WrapperTagName="p" HideIfNoText="false" CssClass="sfDescription"></sf:SitefinityLabel>
            <sf:SitefinityLabel ID="exampleLabel_read" runat="server" WrapperTagName="P" HideIfNoText="true" CssClass="sfExample" />
        </sf:ConditionalTemplate>
        <sf:ConditionalTemplate Left="DisplayMode" Operator="Equal" Right="Write" runat="server">
            <sf:SitefinityLabel ID="titleLabel_write" runat="server" CssClass="sfTxtLbl" />
            <asp:LinkButton ID="expandButton_write" runat="server" OnClientClick="return false;" CssClass="sfOptionalExpander" />
            <asp:Panel ID="expandableTarget_write" runat="server" CssClass="sfFieldWrp">
                 
                <sf:EditorContentManagerDialog runat="server" ID="asyncImageSelector" DialogMode="Document" Width="540" HostedInRadWindow="false"  BodyCssClass="" />
                <asp:TextBox ID="textBox_write" runat="server" CssClass="sfTxt" />
                <asp:LinkButton ID="replaceImage" OnClientClick="return false;" runat="server" CssClass="sfLinkBtn sfChange">
                <span class="sfLinkBtnIn"><asp:Literal runat="server" ID="AddImageLiteral" Text="Select..." /></span>
            </asp:LinkButton>
                 
                <sf:SitefinityLabel id="descriptionLabel_write" runat="server" WrapperTagName="div" HideIfNoText="true" CssClass="sfDescription" />
                <sf:SitefinityLabel id="exampleLabel_write" runat="server" WrapperTagName="div" HideIfNoText="true" CssClass="sfExample" />
            </asp:Panel>
        </sf:ConditionalTemplate>
    </Templates>
</sf:ConditionalTemplateContainer>

As you can see I replaced the <img> control in Read Mode with a Sitefinity Hyperlink,which will be used as a link to the document and I also replaced the Rad WindowManager with the EditorContentManagerDialog.
Then in the codefile of my field, I added references to the new control, removed the old ones and changed the way Documents are displayed in Read mode:

public override object Value
        {
            get
            {
                var val = string.Empty;
                switch (this.DisplayMode)
                {
                    case FieldDisplayMode.Read:
                        val = this.DocumentLink.NavigateUrl;
                        break;
                    case FieldDisplayMode.Write:
                        val = this.TextBoxControl.Text;
                        break;
                }
                return val;
            }
            set
            {
                if (this.ChildControlsCreated)
                {
                    switch (this.DisplayMode)
                    {
                        case FieldDisplayMode.Write:
                            this.TextBoxControl.Text = value as string;
                            break;
 
                        case FieldDisplayMode.Read:
                            var imageId = new Guid(value.ToString());
                            var document = App.WorkWith().Document(imageId).Get();
                            this.DocumentLink.NavigateUrl = document.Url;
                            this.DocumentLink.Text = document.Title;
                            
                            break;
                    }
                    base.Value = null;
                }
                else
                {
                    base.Value = value;
                }
            }
        }

protected EditorContentManagerDialog AsyncImageSelector
        {
            get
            {
                return this.Container.GetControl<EditorContentManagerDialog>("asyncImageSelector", false);
            }
        }

and I also added the dialog to the ScriptDescriptors, so I can have a reference object of it inside the client component:

public override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
        {
            var lastDescriptor = (ScriptControlDescriptor)base.GetScriptDescriptors().Last();
 
            if (this.DisplayMode == FieldDisplayMode.Write)
            {
                
                lastDescriptor.AddElementProperty("replaceImageButtonElement", this.ReplaceImageButton.ClientID);
                lastDescriptor.AddComponentProperty("asyncImageSelector", this.AsyncImageSelector.ClientID);
                
            }
            if (this.DisplayMode == FieldDisplayMode.Read)
            {
                lastDescriptor.AddElementProperty("imageControl", this.DocumentLink.ClientID);
            }
 
            yield return lastDescriptor;
        }

Finally, I modified the javascript part of the field to get the selected item from the dialog and set its ID as a Value of the field control:

On initialize I create all the needed handlers and delegates (and then remove them on dispose):

initialize: function () {
        Telerik.Sitefinity.Samples.SimpleImageField.callBaseMethod(this, "initialize");
        this._replaceImageButtonElementClickDelegate = Function.createDelegate(this, this._replaceImageButtonElementClicked);
        if (this._replaceImageButtonElement) {
            $addHandler(this._replaceImageButtonElement, "click", this._replaceImageButtonElementClickDelegate);
        }
 
        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);
 
        if (this._asyncImageSelector) {
            this._uploadDialog = jQuery(this._asyncImageSelector.get_element()).dialog({
                autoOpen: false,
                modal: true,
                width: 540,
                height: "auto",
                closeOnEscape: true,
                resizable: false,
                draggable: false,
                zIndex: 5000,
                dialogClass: "sfSelectorDialog"
            });
 
 
            this._asyncImageSelectorInsertDelegate = Function.createDelegate(this, this._asyncImageSelectorInsertHandler);
            this._asyncImageSelector.set_customInsertDelegate(this._asyncImageSelectorInsertDelegate);
        }
 
 
    },

The tree delegates are for opening the dialogue on button click (replaceImageButtonElementClicked), setting the Read mode value (_set_readModeValue) and setting the selected item's ID to the value of the field on the dialogue's item selected event (_asyncImageSelectorInsertHandler).

replaceImageButtonElementClicked: function (sender, args) {
 
 
        this._uploadDialog.dialog("open");
        var scrollTopHtml = jQuery("html").eq(0).scrollTop();
        var scrollTopBody = jQuery("body").eq(0).scrollTop();
        var scrollTop = ((scrollTopHtml > scrollTopBody) ? scrollTopHtml : scrollTopBody) + 50;
        jQuery(this._uploadDialog).parent().css({ "top": scrollTop });
        try {
            this._asyncImageSelector.get_uploaderView().get_altTextField().set_value("");
        }
        catch (ex) { }
        jQuery(this._asyncImageSelector.get_uploaderView().get_settingsPanel()).hide();
 
        return false;
    },
 
 
 
    /* -------------------- private methods ----------- */
 
    _set_readModeValue: function (value) {
        if (value === undefined || value == null) {
            this._clearLabel();
        }
 
    },
 
    _asyncImageSelectorInsertHandler: function (selectedItem) {
 
        if (selectedItem) {
            this.set_value(selectedItem.Id);
            this._uploadDialog.dialog("close");
 
 
        }
    },

This can be easily turned into an image selector, as I've already stated, by just changing the DialogMode property of the EditorContentManager dialog to "Image" and changing the displaying controls for Read Mode.

Installation instructions (basically they are almost the same as in Slavo's post):

1) Extract the files into a folder
2) Add them as an external project to your Sitefinity solution
3) Copy/paste the global.asax file into SitefinityWebApp (or if you already have an existing one, add the code to it) or  manually register a virtual path ~/ThumbnailSelector/* (this is used for the template of the control). It can be done from the Backend -> Administration >> Settings >> Advanced >> VirtualPathSettings.
4) Set the build action for the .ascx and .js files to EmbeddedResource
5) Add a reference to the control's project in SitefinityWebApp
6) Add the .js file as a web resource in AssemblyInfo.cs of SitefinityWebApp:

[assembly: WebResource("SitefinityWebApp.SimpleImageField.SimpleImageField.js", "application/x-javascript")]

7) Compile and run your project

8) Create a custom field in any module (works for ECommerce, as well) and specify a custom widget for entering data. The type of the custom widget should be Telerik.Sitefinity.Samples.SimpleImageField

9) For read mode, you can add the SimpleImageField in your content-widget templates like this (assuming the field is called Thumbnail):

<samples:SimpleImageField runat="server" DisplayMode="Read" Value='<%# Eval("Thumbnail")%>' />

Download the control from here:

DocumentSelectorField

3 comments

Leave a comment
  1. Mutantmannen Jun 15, 2012
    When I ad this to a product type in the E-commerce module, what field/database type do I select?

    OC
  2. Martin Oct 25, 2012
    Short text will work for you without a problem.
  3. Faramarz Jun 28, 2013
    Does this solution also work for Sitefinity 6.0?  I am getting 4 errors after step 6, after adding the code to my AssemblyInfo.cs file and building my website.

    Error 1 The type 'Telerik.OpenAccess.SPI.dataobjects.PersistenceCapable' is defined in an assembly that is not referenced. You must add a reference to assembly 'Telerik.OpenAccess, Version=2011.2.713.3, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342'. C:\inetpub\wwwroot\DocumentSelectorField\SimpleImageField\SimpleImageField.cs 89 29 ThumbnailSelectorField

    Error 2 The type 'Telerik.OpenAccess.IInstanceCallbacks' is defined in an assembly that is not referenced. You must add a reference to assembly 'Telerik.OpenAccess, Version=2011.2.713.3, Culture=neutral, PublicKeyToken=7ce17eeaf1d59342'. C:\inetpub\wwwroot\DocumentSelectorField\SimpleImageField\SimpleImageField.cs 89 29 ThumbnailSelectorField

    Error 3 The type or namespace name 'WebResourceAttribute' could not be found (are you missing a using directive or an assembly reference?) C:\inetpub\wwwroot\Sitefinity_DEVTest\SitefinityDEV\Properties\AssemblyInfo.cs 39 12 SitefinityWebApp

    Error 4 The type or namespace name 'WebResource' could not be found (are you missing a using directive or an assembly reference?) C:\inetpub\wwwroot\Sitefinity_DEVTest\SitefinityDEV\Properties\AssemblyInfo.cs 39 12 SitefinityWebApp

    Leave a comment