More Usable Custom WebEditor

1 posts, 0 answers
  1. Jaime Weise
    Jaime Weise avatar
    25 posts
    Member since:
    Nov 2008

    Posted 27 Jul 2010 Link to this post

    Requirements

    RadControls version

    .NET version

    Visual Studio version

    programming language

    browser support

    all browsers supported by RadControls


    WebEditor for UserControl Public Properties
    The architecture of ASP.NET's UserControls coupled with Sitefinity's ability to create a drag and drop a custom control onto the designer at runtime is absolutely delightful. However, 
    One of the problems with creating custom user controls is that it is difficult for your user to add text and image content in a wysiwyg fashion. There simple examples of usercontrols that employ a custom webeditor but in reality they have left much to be desired. I have found that the images don't work in the editor and the styles are very messed up.  I was able to solve most of these issues and get a more desired(ie. usable) result without a huge amount of up-front effort. 

    By using the LoadControl function I was able to instantiate the control that was used in the ControlTemplates directory for the RadEditor. Also I update the css to make the image dialogs show up. If you examine the following code you will get a feel for what I have done and be able to apply it to different controls that will really make easy the process of authoring and getting your websites into the customers hands. You will also be able to have usable controls at the same time. Too good to be true? 

    The following code can be put in the App_Code folder but I recommend creating a separate project for it unless you can compile Sitefinity in 5 seconds. If you are doing work on the control the whole website has to be rebuilt whenever you make changes. 

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using Telerik.Web.UI;
    using Telerik.Cms.Engine.WebControls.Design;
    using System.Web.UI;
    using Telerik.Cms.Engine.WebControls;

    public class CustomTextWebEditor : Telerik.Cms.Web.UI.WebUITypeEditor<string>
    {
        public CustomTextWebEditor()
        {
            this.EnsureChildControls();
        }
        public override string Value
        {
            get
            {
                return _RadEditor.Content;
            }
            set
            {
                _value = value;
                if (_value != _RadEditor.Content)
                {
                    _RadEditor.Content = value;
                }
            }
        }
     
        protected override void CreateChildControls()
        {
            Controls.Clear();
            _RadEditor = new RadEditor();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            control = this.TemplateControl.LoadControl("~/UserControls/CustomEditor.ascx");
            this.Controls.Add(control);
            _RadEditor = control.FindControl("Editor") as RadEditor;      
            this.LoadControlState(this);
        }

        protected override void OnPreRender(EventArgs e)
        {
            base.OnPreRender(e);
            this.SaveControlState();
        }
        private Control control;
        private RadEditor _RadEditor = null;
        private string _value = null;
    }

    First we use the TemplateControl.LoadControl("~/UserControls/CustomEditor.ascx") to load the custom editor into memory and then we get a reference the the RadEditor control for later use. I am not sure we need the CreatChildControls override but it was leftover from the code example where I started from. The only thing left was to save and restore the control state. 

    The next part is to have a control in the ~/UserControls/ directory to load. I used the GenericContent edit template as a base to start from the UserControl code looks like this.

    <%@ Control Language="C#" ClassName="CustomEditor" %>
    <style type="text/css">
        .Dialog
        {
            MARGIN-LEFT: -64px! important;
            WIDTH: 748px! important
        }
        .Dialog fieldset
        {
            height:420px !important;
        }
        .RadWindow
        {
            z-index:15001 !important;
        }
        .rwTable
        {
            height:498px !important;
            margin-top:0px !important;
        }
    </style>
    <%@ Register TagPrefix="sf" Namespace="Telerik.Cms.Engine.WebControls" Assembly="Telerik.Cms.Engine" %>
    <%@ Register TagPrefix="sf" Namespace="Telerik.Cms.Engine.WebControls.Admin" Assembly="Telerik.Cms.Engine" %>
    <%@ Register TagPrefix="sf" Namespace="Telerik.Cms.Web.UI" Assembly="Telerik.Cms.Web.UI" %>
     
     
                    <telerik:RadEditor
                        id="Editor"
                        runat="server"
                        contentareacssfile="~/Sitefinity/Admin/Themes/Default/AjaxControlsSkins/Sitefinity/EditorContentArea.css"
                        toolsfile="~/Sitefinity/Admin/ControlTemplates/EditorToolsFile.xml"
                        skin="WebBlue"
                        newlinebr="False"
                        width="95%"
                        ContentFilters="EncodeScripts,FixUlBoldItalic,FixEnclosingP,IECleanAnchors,MozEmStrong,ConvertFontToSpan,ConvertToXhtml,IndentHTMLContent">
                            <ImageManager ViewPaths="~/Images" UploadPaths="~/Images" DeletePaths="~/Images" />
                            <MediaManager ViewPaths="~/Files" UploadPaths="~/Files" DeletePaths="~/Files" />
                            <FlashManager ViewPaths="~/Files" UploadPaths="~/Files" DeletePaths="~/Files" />
                            <DocumentManager ViewPaths="~/Files" UploadPaths="~/Files" DeletePaths="~/Files" />
                            <CssFiles>
                                <telerik:EditorCssFile Value="~/Sitefinity/Admin/Themes/Default/AjaxControlsSkins/Sitefinity/EditorCssFile.css" />
                            </CssFiles>
                        </telerik:RadEditor>
     
                    <script type="text/javascript">
     
                        Telerik.Web.UI.Editor.CommandList["LibraryImageManager"] = function(commandName, editor, args) {
                            var editorArgs = editor.getSelectedElement();
                            if (!editorArgs.nodeName || typeof (editorArgs.nodeName) == "undefined" || editorArgs.nodeName != "A")
                                editorArgs = editor.getSelection();
     
                            var myCallbackFunction = function(sender, args) {
                                if (typeof (editorArgs.nodeName) != "undefined" && editorArgs.nodeName == "IMG")
                                    args.parentNode.replaceChild(editorArgs, args);
                                else {
                                    var cloned = args.cloneNode(true);
                                    var div = args.ownerDocument.createElement("DIV");
                                    div.appendChild(cloned);
                                    editorArgs.pasteHtml(div.innerHTML);
                                }
                            }
                            editor.showExternalDialog(
                                   '<%= ((Telerik.Cms.Web.CmsPageBase)Page).ResolveCmsUrl("~/Sitefinity/UserControls/Dialogs/ImageEditorDialog.aspx") %>',
                                   editorArgs,
                                   750,
                                   600,
                                   myCallbackFunction,
                                   null,
                                   'ImageLibraryDialog',
                                   true,
                                   Telerik.Web.UI.WindowBehaviors.Close + Telerik.Web.UI.WindowBehaviors.Move,
                                   false,
                                   true)
                        };
     
                        Telerik.Web.UI.Editor.CommandList["LibraryDocumentManager"] = function(commandName, editor, args) {
                            var editorArgs = editor.getSelectedElement();
                            if (!editorArgs.nodeName || typeof (editorArgs.nodeName) == "undefined" || editorArgs.nodeName != "A")
                                editorArgs = editor.getSelection();
     
                            var myCallbackFunction = function(sender, args) {
                                if (typeof (editorArgs.nodeName) != "undefined" && editorArgs.nodeName == "A")
                                    args.parentNode.replaceChild(editorArgs, args);
                                else {
                                    var cloned = args.cloneNode(true);
                                    var div = args.ownerDocument.createElement("DIV");
                                    div.appendChild(cloned);
                                    editorArgs.pasteHtml(div.innerHTML);
                                }
                            }
                            editor.showExternalDialog(
                                   '<%= ((Telerik.Cms.Web.CmsPageBase)Page).ResolveCmsUrl("~/Sitefinity/UserControls/Dialogs/DocumentEditorDialog.aspx") %>',
                                   editorArgs,
                                   750,
                                   600,
                                   myCallbackFunction,
                                   null,
                                   'ImageLibraryDialog',
                                   false,
                                   Telerik.Web.UI.WindowBehaviors.Close + Telerik.Web.UI.WindowBehaviors.Move,
                                   false,
                                   true)
                        };
     
                        Telerik.Web.UI.Editor.CommandList["LinkManager"] = function(commandName, editor, args) {
                            var editorArgs = editor.getSelectedElement();
                            if (!editorArgs.nodeName || typeof (editorArgs.nodeName) == "undefined" || editorArgs.nodeName != "A") {
                                var sel = editor.getSelection();
                                editorArgs = sel;
                                editorArgs.Html = sel.getHtml();
                                editorArgs.Text = sel.getText();
                            }
     
                            var myCallbackFunction = function(sender, args) {
                                if (typeof (editorArgs.nodeName) != "undefined" && editorArgs.nodeName == "A")
                                    args.parentNode.replaceChild(editorArgs, args);
                                else {
                                    var cloned = args.cloneNode(true);
                                    var div = args.ownerDocument.createElement("DIV");
                                    div.appendChild(cloned);
                                    editorArgs.pasteHtml(div.innerHTML);
                                }
                            }
                            editor.showExternalDialog(
                                   '<%= ((Telerik.Cms.Web.CmsPageBase)Page).ResolveCmsUrl("~/Sitefinity/UserControls/Dialogs/LinksDialog.aspx") %>',
                                   editorArgs,
                                   750,
                                   600,
                                   myCallbackFunction,
                                   null,
                                   'ImageLibraryDialog',
                                   false,
                                   Telerik.Web.UI.WindowBehaviors.Close + Telerik.Web.UI.WindowBehaviors.Move,
                                   false,
                                   true)
                        };
     
                        Telerik.Web.UI.Editor.CommandList["SetLinkProperties"] = function(commandName, editor, args) {
                            var editorArgs = editor.getSelectedElement();
                            if (!editorArgs.nodeName || typeof (editorArgs.nodeName) == "undefined" || editorArgs.nodeName != "A")
                                editorArgs = editor.getSelection();
     
                            var myCallbackFunction = function(sender, args) {
                                if (typeof (editorArgs.nodeName) != "undefined" && editorArgs.nodeName == "A")
                                    args.parentNode.replaceChild(editorArgs, args);
                                else {
                                    var cloned = args.cloneNode(true);
                                    var div = args.ownerDocument.createElement("DIV");
                                    div.appendChild(cloned);
                                    editorArgs.pasteHtml(div.innerHTML);
                                }
                            }
                            editor.showExternalDialog(
                                   '<%= ((Telerik.Cms.Web.CmsPageBase)Page).ResolveCmsUrl("~/Sitefinity/UserControls/Dialogs/LinksDialog.aspx") %>',
                                   editorArgs,
                                   750,
                                   600,
                                   myCallbackFunction,
                                   null,
                                   'ImageLibraryDialog',
                                   false,
                                   Telerik.Web.UI.WindowBehaviors.Close + Telerik.Web.UI.WindowBehaviors.Move,
                                   false,
                                   true)
                        };
     
                        var oldFunction = Telerik.Web.UI.Editor.CommandList["ToggleScreenMode"]; //save the original Paste function
     
                        Telerik.Web.UI.Editor.CommandList["ToggleScreenMode"] = function(commandName, editor, args) {
                            oldFunction(commandName, editor, args);
                            var bd = document.getElementsByTagName("body")[0];
     
                            if (/fullScreenMode/.test(bd.className)) {
                                var rep = bd.className.match(' ' + 'fullScreenMode') ? ' ' + 'fullScreenMode' : 'fullScreenMode';
                                bd.className = bd.className.replace(rep, '');
     
                            } else {
                                bd.className += bd.className ? ' ' + 'fullScreenMode' : 'fullScreenMode';
                            }
                        };
     
                        // automated tests helper function
                        function InsertTextArea() {
                            var editor = $find('<%=Editor.ClientID%>');
                            editor.set_html('<textarea id="myTableToFind" style="overflow:hidden; height: 300px; width: 500px;" border="none"></textarea>');
                        }
     
     
                    </script>

    Not much to explain here but to put it into a UserControl Called  CustomEditor.ascx. The other part is that understand that the name of the Editor matters ("Editor") in this example. It is used in the custom WebEditor and there is a Editor.ClientID reference in the javascript. You can put this control in your sitefinity project (not in a separate project).

    Now to wrap things up you Build a custom UserControl of your choice and add a public property.

    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="next-meeting-display.ascx.cs" Inherits="TEMP_next_meeting_display" %>
         
        <div id="next_meeting">
        <table>
        <tr>
            <th colspan="2"><h1><a href="/activities/open-meeting.aspx">This Week's Vancouver Open Meeting</a></h1></th>
     
        </tr>
            <tr>
                <td><u>Venue:</u></td>
                <td><%# Venue %></td>
            </tr>
            <tr>
                <td><u>Speaker:</u></td>
                <td><%# Speaker %></td>
            </tr>
             
            </table>
         
        </div>

    and the code file.
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
     
    public partial class TEMP_next_meeting_display : System.Web.UI.UserControl
    {
        [Telerik.Cms.Web.UI.WebEditor("CustomTextWebEditor, EIO.CustomEditor")]
        public String Venue { get; set; }
        public String Speaker { get; set; }
        protected void Page_Load(object sender, EventArgs e)
        {
            DataBind();
        }
    }

    Here you can see that I have added a custom WebEditor attribute to the property Venue.

    The end result is that when you add your UserControl to sitefinity and drag it onto a content area you can select edit and you will have a button that pops up a nice editor to fill in the Venue value for the user control. This is something that is not available out of the box but is a great addition to the drag and drop support that Sitefinity has so conveniently added. 


    I will add to this later and make it more clear. If you have any difficulties implementing this please post a comment. Maybe if someone wants to wrap up the usercontrol part of the sample into the external project so that it is a nice managed dll that would be an excellent feat. 

Back to Top