RadDocks & Dynamic TabStrip

6 posts, 2 answers
  1. Chris
    Chris avatar
    58 posts
    Member since:
    Feb 2012

    Posted 11 Apr 2013 Link to this post

    Hi there.

    What I'm trying to achieve here is to get a dynamic TabStrip to which I can create new tabs.
    Each of those tabs include a pageview.
    Each of those pageview embeds a usercontrol.
    Those usercontrols are consisting in a RadDockLayout + RadDockZone + a Button to create new docks on the current selected tab/pageview.

    So far nothing that sounds too fancy or hard to create. The current code works fine:

    Here's my "Default.aspx". You can notice that I'm not currently using the RadAjaxManager to Ajaxify my controls.
    In this page I'm creating my dynamic TabStrip control that let you create and close tabs via a close button.

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Default" %>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <head runat="server">
        <title></title>
        <link href="css/lines.css" rel="stylesheet" type="text/css" />
        <link href="css/colorpicker.css" rel="stylesheet" type="text/css" />
        <telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server" />
    </head>
    <body>
        <form id="form1" runat="server">
        <telerik:RadScriptManager ID="RadScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.Core.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQuery.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQueryInclude.js" />
            </Scripts>
        </telerik:RadScriptManager>
     
        <script src="js/colorpicker.js" type="text/javascript"></script>
     
        <telerik:RadCodeBlock ID="rcbLineManager" runat="server">
     
            <script type="text/javascript">
                function closeTab(tabText) {
                    try {
                        var tabStrip = $find("<%= rtsLines.ClientID %>");
                        var multiPage = $find("<%= mpLines.ClientID %>");
                        var ajaxManager = $find("<%= RadAjaxManager1.ClientID %>");
                        var tab = tabStrip.findTabByText(tabText);
                        var pageView = tab.get_pageView();
     
                        var tabToSelect = tab.get_nextTab();
                        if (!tabToSelect)
                            tabToSelect = tab.get_previousTab();
     
                        tabStrip.get_tabs().remove(tab);
                        multiPage.get_pageViews().remove(pageView);
                        if (ajaxManager != null) {
                            ajaxManager.ajaxRequest("CloseTab|" + tabText);
                        } else {
                            console.log("ajaxManager is null");
                        }
     
                        if (tabToSelect)
                            tabToSelect.set_selected(true);
                    }
                    catch (err) {
                        console.log(err.message);
                    }
                }
            </script>
     
        </telerik:RadCodeBlock>
        <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server" OnAjaxRequest="RadAjaxManager1_AjaxRequest">
    <%--        <AjaxSettings>
                <telerik:AjaxSetting AjaxControlID="rbtAddLine">
                    <UpdatedControls>
                        <telerik:AjaxUpdatedControl ControlID="rtsLines"/>
                        <telerik:AjaxUpdatedControl ControlID="mpLines" LoadingPanelID="ralpLines" />
                    </UpdatedControls>
                </telerik:AjaxSetting>
            </AjaxSettings>--%>
        </telerik:RadAjaxManager>
        <telerik:RadButton ID="rbtAddLine" runat="server" OnClick="rbtAddLine_Clicked" Text="New">
        </telerik:RadButton>
        <div>
            <telerik:RadTabStrip ID="rtsLines" runat="server" CssClass="rtsLines" MultiPageID="mpLines"
                OnTabCreated="rtsLines_TabCreated">
            </telerik:RadTabStrip>
            <telerik:RadMultiPage ID="mpLines" runat="server" CssClass="multiPageViewContent"
                Height="400" Width="900" OnPageViewCreated="mpLines_PageViewCreated">
            </telerik:RadMultiPage>
            <telerik:RadAjaxLoadingPanel ID="ralpLines" runat="server">
            </telerik:RadAjaxLoadingPanel>
        </div>
        </form>
    </body>
    </html>


    Here's the code behind:
    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Data;
    using System.Configuration;
    using System.Web.Security;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    using Telerik.Web.UI;
    using System.Collections.Generic;
    using TabsStrips.usercontrol;
     
    public partial class Default : System.Web.UI.Page
    {
        private static int _id = 0;
        private string tabName;
     
     
        protected void Page_Load(object sender, EventArgs e)
        {
             
        }
     
        protected void rbtAddLine_Clicked(object sender, EventArgs e)
        {
            try
            {
                tabName = "New_" + _id++;
                rtsLines.Tabs.Add(new RadTab(tabName));
                var pv = new RadPageView();
                pv.ID = "pv" + tabName;
                pv.Height = Unit.Pixel(200);
                pv.Width = Unit.Pixel(800);
                mpLines.PageViews.Add(pv);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
     
        protected void rtsLines_TabCreated(object sender, RadTabStripEventArgs e)
        {
            try
            {
                HtmlImage img1 = new HtmlImage();
                img1.Src = "~/images/close.gif";
                img1.Attributes.Add("class""closeTab");
                Label lbl = new Label();
                lbl.Text = e.Tab.Text;
                img1.Attributes.Add("onclick""closeTab('" + e.Tab.Text + "');");
     
                e.Tab.Controls.Add(img1);
                e.Tab.Controls.Add(lbl);
            }
            catch (Exception ex)
            {           
                throw;
            }
        }
     
        protected void mpLines_PageViewCreated(object sender, RadMultiPageEventArgs e)
        {
            UserControl LineBuilder = (UserControl)this.LoadControl("usercontrol/LineBuilderV2.ascx");
            LineBuilder.ID = "LineBuilder_" + e.PageView.UniqueID;
            e.PageView.Controls.Add(LineBuilder);
        }
     
        protected void RadAjaxManager1_AjaxRequest(object sender, AjaxRequestEventArgs e)
        {
            try
            {
                var command = e.Argument.Split('|')[0];
                var args = e.Argument.Split('|').Length > 0 ? e.Argument.Split('|')[1] : null;
     
                switch (command)
                {
                    case "CloseTab":
                        var tab = rtsLines.FindTabByText(args, true);
                        var pv = tab.PageView;
                        rtsLines.Tabs.Remove(tab);
                        mpLines.PageViews.Remove(pv);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                 
                throw;
            }
        }
    }


    Here's now the content of my usercontrol embedding the RadDockZone:
    Note that I'm using a "hidden" but still "visible" asp update panel to create my new docks, as described in most of the Telerik online examples
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="LineBuilderV2.ascx.cs"
        Inherits="TabsStrips.usercontrol.LineBuilderV2" %>
    <telerik:RadAjaxLoadingPanel ID="RalpLineBuilder" runat="server" MinDisplayTime="500">
    </telerik:RadAjaxLoadingPanel>
    <telerik:RadScriptBlock ID="rsbLine" runat="server">
     
        <script type="text/javascript">
            $(document).ready(function() {
                var elt = $('#<%= PCommands.ClientID %>');
                var stops = $('#<%= RdzLineBuilder.ClientID %>');
                $(elt).find(".colorpicker2").ColorPicker({
                    color: '#0000ff',
                    onShow: function(colpkr) {
                        $(colpkr).fadeIn(500);
                        return false;
                    },
                    onHide: function(colpkr) {
                        $(colpkr).fadeOut(500);
                        return false;
                    },
                    onChange: function(hsb, hex, rgb) {
                        $(elt).find(".colorpicker2 div").css('backgroundColor', '#' + hex);
                        $(stops).find(".<%= RdzLineBuilder.ClientID  %>").css('backgroundColor', '#' + hex);
                    }
                });
     
                var currentLoadingPanel = null;
                var currentUpdatedControl = null;
     
                Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
                Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
                function BeginRequestHandler(sender, args) {
                    currentLoadingPanel = $find("<%= RalpLineBuilder.ClientID %>");
                    currentUpdatedControl = $find("<%= PLineBuilder.ClientID %>");
                    currentLoadingPanel.show(currentUpdatedControl);
                }
     
                function EndRequestHandler(sender, args) {
                    if (currentLoadingPanel != null)
                        currentLoadingPanel.hide(currentUpdatedControl);
                    currentUpdatedControl = null;
                    currentLoadingPanel = null;
                }
                 
            });
        </script>
     
    </telerik:RadScriptBlock>
    <asp:Button ID="ButtonAddStop" runat="server" OnClick="ButtonAddStop_Click" Text="Add Stop" />
    <asp:UpdatePanel ID="UpLineBuilder" runat="server" ChildrenAsTriggers="false" UpdateMode="Conditional" >
        <ContentTemplate>
            <asp:Panel ID="PLineBuilder" runat="server" Width="800px">
                <telerik:RadDockLayout ID="RdlLineBuilder" runat="server" OnSaveDockLayout="RdlLineBuilder_SaveDockLayout"
                    OnLoadDockLayout="RdlLineBuilder_LoadDockLayout">
                    <telerik:RadDockZone ID="RdzLineBuilder" runat="server" Width="100%" Height="200px"
                        Orientation="Horizontal">
                    </telerik:RadDockZone>
                </telerik:RadDockLayout>
            </asp:Panel>
        </ContentTemplate>
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="ButtonAddStop" EventName="Click" />
        </Triggers>
    </asp:UpdatePanel>
    <asp:Panel ID="PCommands" runat="server">
        <div class="colorpicker2">
            <div>
            </div>
        </div>
    </asp:Panel>
    <div style="width: 0px; height: 0px; overflow: hidden; position: absolute; left: -10000px;">
        Hidden UpdatePanel, which is used to help with saving state when minimizing, moving
        and closing docks. This way the docks state is saved faster (no need to update the
        docking zones).
        <asp:UpdatePanel ID="UpLineBuilderHidden" runat="server">
        </asp:UpdatePanel>
    </div>


    Here's the code behind:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;
    using System.Web.UI.HtmlControls;
     
    namespace TabsStrips.usercontrol
    {
        public partial class LineBuilderV2 : System.Web.UI.UserControl
        {
            private string _suffixID;
     
            private List<DockState> CurrentDockStates
            {
                get
                {
                    //Store the info about the added docks in the session. For real life
                    // applications we recommend using database or other storage medium
                    // for persisting this information.
                    List<DockState> _currentDockStates = (List<DockState>)Session["CurrentDockStates_" this.ID];
                    if (Object.Equals(_currentDockStates, null))
                    {
                        _currentDockStates = new List<DockState>();
                        Session["CurrentDockStates_" this.ID] = _currentDockStates;
                    }
                    return _currentDockStates;
                }
                set
                {
                    Session["CurrentDockStates_" this.ID] = value;
                }
            }
     
     
            protected void Page_Load(object sender, EventArgs e)
            {
                _suffixID = this.ClientID.Replace("-""").Replace("$""");
                this.Page.LoadComplete += new EventHandler(Page_LoadComplete);
            }
     
            void Page_LoadComplete(object sender, EventArgs e)
            {
                ScriptManager.RegisterStartupScript(thisthis.GetType(), "LineBuilder_" + _suffixID,
                    @"var currentLoadingPanel = null;
                      var currentUpdatedControl = null;
     
                      Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
                      Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);
                      function BeginRequestHandler(sender, args) {
                          currentLoadingPanel = $('#" + RalpLineBuilder.ClientID + @"');
                          currentUpdatedControl = $('#" + PLineBuilder.ClientID + @"');
                          currentLoadingPanel.show(currentUpdatedControl);
                      }
     
                      function EndRequestHandler(sender, args) {
                          if (currentLoadingPanel != null)
                              currentLoadingPanel.hide(currentUpdatedControl);
                          currentUpdatedControl = null;
                          currentLoadingPanel = null;
                      }", true);
            }
     
            protected void Page_Init(object sender, EventArgs e)
            {
                //Recreate the docks in order to ensure their proper operation
                for (int i = 0; i < CurrentDockStates.Count; i++)
                {
                    RadDock dock = CreateRadDockFromState(CurrentDockStates[i]);
                    //We will just add the RadDock control to the RadDockLayout.
                    // You could use any other control for that purpose, just ensure
                    // that it is inside the RadDockLayout control.
                    // The RadDockLayout control will automatically move the RadDock
                    // controls to their corresponding zone in the LoadDockLayout
                    // event (see below).
                    RdlLineBuilder.Controls.Add(dock);
                    //We want to save the dock state every time a dock is moved.
                    CreateSaveStateTrigger(dock);
     
                    if (CurrentDockStates[i].Closed == true)
                    {
                        dock.Visible = false;
                    }
                }
            }
     
            protected void RdlLineBuilder_LoadDockLayout(object sender, DockLayoutEventArgs e)
            {
                //Populate the event args with the state information. The RadDockLayout control
                // will automatically move the docks according that information.
                foreach (DockState state in CurrentDockStates)
                {
                    e.Positions[state.UniqueName] = state.DockZoneID;
                    e.Indices[state.UniqueName] = state.Index;
                }
            }
     
            protected void RdlLineBuilder_SaveDockLayout(object sender, DockLayoutEventArgs e)
            {
                //Save the dock state in the session. This will enable us
                // to recreate the dock in the next Page_Init.
                CurrentDockStates = RdlLineBuilder.GetRegisteredDocksState();
            }
     
            private RadDock CreateRadDockFromState(DockState state)
            {
                HtmlGenericControl div = new HtmlGenericControl("div");
                div.Attributes.Add("class", RdzLineBuilder.ClientID);
                div.Style.Add("width""100px");
                div.Style.Add("height""100px");
                div.Style.Add("background-color""White");
     
                RadDock dock = new RadDock();
                dock.DockMode = DockMode.Docked;
                dock.ID = string.Format("RadDock{0}", state.UniqueName);
                dock.ApplyState(state);
                dock.ContentContainer.Controls.Add(div);
                dock.Command += new DockCommandEventHandler(dock_Command);
                dock.Commands.Add(new DockCloseCommand());
     
                return dock;
            }
     
            void dock_Command(object sender, DockCommandEventArgs e)
            {
                if (e.Command.Name == "Close")
                {
                    ScriptManager.RegisterStartupScript(
                    UpLineBuilderHidden,
                    this.GetType(),
                    "RemoveDock",
                    string.Format(@"function _removeDock() {{ 
                                    Sys.Application.remove_load(_removeDock); 
                                    $find('{0}').undock(); 
                                    $get('{1}').appendChild($get('{0}')); 
                                    $find('{0}').doPostBack('DockPositionChanged'); 
                                    }}; 
                                    Sys.Application.add_load(_removeDock);", ((RadDock)sender).ClientID, UpLineBuilderHidden.ClientID),
                                  true);
                    CurrentDockStates.Remove(((RadDock)sender).GetState());
                }
            }
     
            private RadDock CreateRadDock()
            {
                HtmlGenericControl div = new HtmlGenericControl("div");
                div.Attributes.Add("class", RdzLineBuilder.ClientID);
                div.Style.Add("width""100px");
                div.Style.Add("height""100px");
                div.Style.Add("background-color""White");
     
                RadDock dock = new RadDock();
                dock.DockMode = DockMode.Docked;
                dock.UniqueName = Guid.NewGuid().ToString().Replace("-""a");
                dock.ID = string.Format("RadDock{0}", dock.UniqueName);
                dock.Title = "Stop";
                dock.Text = string.Format("Added at {0}\n{1}", DateTime.Now, dock.UniqueName);
                dock.ContentContainer.Controls.Add(div);
                dock.Width = Unit.Pixel(220);
                dock.Height = Unit.Pixel(200);
                dock.Command += new DockCommandEventHandler(dock_Command);
                dock.Commands.Add(new DockCloseCommand());
     
                return dock;
            }
     
            private void CreateSaveStateTrigger(RadDock dock)
            {
                //Ensure that the RadDock control will initiate postback
                // when its position changes on the client or any of the commands is clicked.
                //Using the trigger we will "ajaxify" that postback.
                dock.AutoPostBack = true;
                dock.CommandsAutoPostBack = true;
     
                AsyncPostBackTrigger saveStateTrigger = new AsyncPostBackTrigger();
                saveStateTrigger.ControlID = dock.ID;
                saveStateTrigger.EventName = "DockPositionChanged";
                UpLineBuilderHidden.Triggers.Add(saveStateTrigger);
     
                saveStateTrigger = new AsyncPostBackTrigger();
                saveStateTrigger.ControlID = dock.ID;
                saveStateTrigger.EventName = "Command";
                UpLineBuilderHidden.Triggers.Add(saveStateTrigger);
            }
     
            protected void ButtonAddStop_Click(object sender, EventArgs e)
            {
                RadDock dock = CreateRadDock();
                //adding the dock to the docklayout and then docking it to the zone to avoid ViewState issues on subsequent postback
                RdlLineBuilder.Controls.Add(dock);
                //UpLineBuilderHidden.ContentTemplateContainer.Controls.Add(dock);
     
                ScriptManager.RegisterStartupScript(
                dock,
                this.GetType(),
                "AddDock",
                string.Format(@"function _addDock() {{ 
                                    Sys.Application.remove_load(_addDock); 
                                    $find('{1}').dock($find('{0}'),{2});  
                                    $find('{0}').doPostBack('DockPositionChanged'); 
                                    }}; 
                                    Sys.Application.add_load(_addDock);", dock.ClientID, RdzLineBuilder.ClientID, 0),
                                                                        true);
     
                CreateSaveStateTrigger(dock);
            }
        }
    }


    As I said, this works fine. But what I want to achieve is to ajaxify the whole lot, i.e Use my RadAjaxManager on my default.aspx page.
    Right now, as soon as I create a new tab, it postback the page and I'm losing some info (plus the page flicks on Chrome, FF is alright)

    If I enable my AjaxManager I get the following error message that plenty of people have seen before:
    "Sys.InvalidOperationException: Sys.InvalidOperationException: Could not find UpdatePanel with ID 'LineBuilder_pvNew_0_UpLineBuilderHidden'. If it is being updated dynamically then it must be inside another UpdatePanel"

    Although I've found many people with the same issue of having an Update Panel within an Ajaxified usercontrol (see here for Telerik explanation) I can't get my mind around this to get a fully ajaxify project.

    Another behavior I would like to code is to get an horizontal scrolling bar to appear when adding new docks rather than having them stacking on a new row and a vertical scroll bar.

    You can download the project source as zip from here

    Any idea / help would be much appreciated.

    Cheers everyone,

    Chris
  2. Chris
    Chris avatar
    58 posts
    Member since:
    Feb 2012

    Posted 12 Apr 2013 Link to this post

    Ok, As I suspected the issue came from my Update Panels in my usercontrols. So I got rid of those and registered my Button, Panel and Loading panel with the RadAjaxManager from my main page. Here's how my usercontrol looks like now:

    Note that there is no more UpdatePanels
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="LineBuilderV2.ascx.cs"
        Inherits="TabsStrips.usercontrol.LineBuilderV2" %>
    <telerik:RadAjaxLoadingPanel ID="RalpLineBuilder" runat="server" MinDisplayTime="500">
    </telerik:RadAjaxLoadingPanel>
    <telerik:RadScriptBlock ID="rsbLine" runat="server">
     
        <script type="text/javascript">
            $(document).ready(function() {
                var elt = $('#<%= PCommands.ClientID %>');
                var stops = $('#<%= RdzLineBuilder.ClientID %>');
                $(elt).find(".colorpicker2").ColorPicker({
                    color: '#0000ff',
                    onShow: function(colpkr) {
                        $(colpkr).fadeIn(500);
                        return false;
                    },
                    onHide: function(colpkr) {
                        $(colpkr).fadeOut(500);
                        return false;
                    },
                    onChange: function(hsb, hex, rgb) {
                        $(elt).find(".colorpicker2 div").css('backgroundColor', '#' + hex);
                        $(stops).find(".<%= RdzLineBuilder.ClientID  %>").css('backgroundColor', '#' + hex);
                    }
                });
                 
            });
        </script>
     
    </telerik:RadScriptBlock>
    <asp:Button ID="ButtonAddStop" runat="server" OnClick="ButtonAddStop_Click" Text="Add Stop" />
            <asp:Panel ID="PLineBuilder" runat="server" Width="800px">
                <telerik:RadDockLayout ID="RdlLineBuilder" runat="server" OnSaveDockLayout="RdlLineBuilder_SaveDockLayout"
                    OnLoadDockLayout="RdlLineBuilder_LoadDockLayout">
                    <telerik:RadDockZone ID="RdzLineBuilder" runat="server" Width="100%" Height="200px"
                        Orientation="Horizontal">
                    </telerik:RadDockZone>
                </telerik:RadDockLayout>
            </asp:Panel>
    <asp:Panel ID="PCommands" runat="server">
        <div class="colorpicker2">
            <div>
            </div>
        </div>
    </asp:Panel>


    And in code behind:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using Telerik.Web.UI;
    using System.Web.UI.HtmlControls;
     
    namespace TabsStrips.usercontrol
    {
        public partial class LineBuilderV2 : System.Web.UI.UserControl
        {
            private string _suffixID;
     
            private List<DockState> CurrentDockStates
            {
                get
                {
                    //Store the info about the added docks in the session. For real life
                    // applications we recommend using database or other storage medium
                    // for persisting this information.
                    List<DockState> _currentDockStates = (List<DockState>)Session["CurrentDockStates_" + this.ID];
                    if (Object.Equals(_currentDockStates, null))
                    {
                        _currentDockStates = new List<DockState>();
                        Session["CurrentDockStates_" + this.ID] = _currentDockStates;
                    }
                    return _currentDockStates;
                }
                set
                {
                    Session["CurrentDockStates_" + this.ID] = value;
                }
            }
     
     
            protected void Page_Load(object sender, EventArgs e)
            {
                _suffixID = this.ClientID.Replace("-", "").Replace("$", "");
            }
     
            protected void Page_Init(object sender, EventArgs e)
            {
                RadAjaxManager.GetCurrent(this.Page).AjaxSettings.AddAjaxSetting(ButtonAddStop, PLineBuilder, RalpLineBuilder);
                //Recreate the docks in order to ensure their proper operation
                for (int i = 0; i < CurrentDockStates.Count; i++)
                {
                    RadDock dock = CreateRadDockFromState(CurrentDockStates[i]);
                    //We will just add the RadDock control to the RadDockLayout.
                    // You could use any other control for that purpose, just ensure
                    // that it is inside the RadDockLayout control.
                    // The RadDockLayout control will automatically move the RadDock
                    // controls to their corresponding zone in the LoadDockLayout
                    // event (see below).
                    RdlLineBuilder.Controls.Add(dock);
                    //We want to save the dock state every time a dock is moved.
                    CreateSaveStateTrigger(dock);
                    if (CurrentDockStates[i].Closed == true)
                    {
                        dock.Visible = false;
                    }
                }
            }
     
            protected void RdlLineBuilder_LoadDockLayout(object sender, DockLayoutEventArgs e)
            {
                //Populate the event args with the state information. The RadDockLayout control
                // will automatically move the docks according that information.
                foreach (DockState state in CurrentDockStates)
                {
                    e.Positions[state.UniqueName] = state.DockZoneID;
                    e.Indices[state.UniqueName] = state.Index;
                }
            }
     
            protected void RdlLineBuilder_SaveDockLayout(object sender, DockLayoutEventArgs e)
            {
                //Save the dock state in the session. This will enable us
                // to recreate the dock in the next Page_Init.
                CurrentDockStates = RdlLineBuilder.GetRegisteredDocksState();
            }
     
            private RadDock CreateRadDockFromState(DockState state)
            {
                HtmlGenericControl div = new HtmlGenericControl("div");
                div.Attributes.Add("class", RdzLineBuilder.ClientID);
                div.Style.Add("width", "100px");
                div.Style.Add("height", "100px");
                div.Style.Add("background-color", "White");
     
                RadDock dock = new RadDock();
                dock.DockMode = DockMode.Docked;
                dock.ID = string.Format("RadDock{0}", state.UniqueName);
                dock.ApplyState(state);
                dock.ContentContainer.Controls.Add(div);
                dock.Command += new DockCommandEventHandler(dock_Command);
                dock.Commands.Add(new DockCloseCommand());
     
                return dock;
            }
     
            void dock_Command(object sender, DockCommandEventArgs e)
            {
                if (e.Command.Name == "Close")
                {
                    ScriptManager.RegisterStartupScript(
                    PLineBuilder,
                    this.GetType(),
                    "RemoveDock",
                    string.Format(@"function _removeDock() {{ 
                                    Sys.Application.remove_load(_removeDock); 
                                    $find('{0}').undock(); 
                                    $get('{1}').appendChild($get('{0}')); 
                                    $find('{0}').doPostBack('DockPositionChanged'); 
                                    }}; 
                                    Sys.Application.add_load(_removeDock);", ((RadDock)sender).ClientID, PLineBuilder.ClientID),
                                  true);
                    CurrentDockStates.Remove(((RadDock)sender).GetState());
                }
            }
     
            private RadDock CreateRadDock()
            {
                HtmlGenericControl div = new HtmlGenericControl("div");
                div.Attributes.Add("class", RdzLineBuilder.ClientID);
                div.Style.Add("width", "100px");
                div.Style.Add("height", "100px");
                div.Style.Add("background-color", "White");
     
                RadDock dock = new RadDock();
                dock.DockMode = DockMode.Docked;
                dock.UniqueName = Guid.NewGuid().ToString().Replace("-", "a");
                dock.ID = string.Format("RadDock{0}", dock.UniqueName);
                dock.Title = "Stop";
                dock.Text = string.Format("Added at {0}\n{1}", DateTime.Now, dock.UniqueName);
                dock.ContentContainer.Controls.Add(div);
                dock.Width = Unit.Pixel(220);
                dock.Height = Unit.Pixel(200);
                dock.Command += new DockCommandEventHandler(dock_Command);
                dock.Commands.Add(new DockCloseCommand());
     
                return dock;
            }
     
            private void CreateSaveStateTrigger(RadDock dock)
            {
                //Ensure that the RadDock control will initiate postback
                // when its position changes on the client or any of the commands is clicked.
                //Using the trigger we will "ajaxify" that postback.
                dock.AutoPostBack = true;
                dock.CommandsAutoPostBack = true;
                // still need to work on that part
            }
     
     
            protected void ButtonAddStop_Click(object sender, EventArgs e)
            {
                RadDock dock = CreateRadDock();
                //adding the dock to the docklayout and then docking it to the zone to avoid ViewState issues on subsequent postback
                RdlLineBuilder.Controls.Add(dock);
                dock.Dock(RdzLineBuilder);
     
                ScriptManager.RegisterStartupScript(
                dock,
                this.GetType(),
                "AddDock",
                string.Format(@"function _addDock() {{ 
                                    Sys.Application.remove_load(_addDock); 
                                    $find('{1}').dock($find('{0}'),{2});  
                                    $find('{0}').doPostBack('DockPositionChanged'); 
                                    }}; 
                                    Sys.Application.add_load(_addDock);", dock.ClientID, RdzLineBuilder.ClientID, 0),
                                                                        true);
     
                CreateSaveStateTrigger(dock);
            }
        }
    }

    Works like a charm now.

    Now I need to make this TabStrip persistent on refresh. Does anyone kow if the new Persistent Framework can be of any help here ?

    Thanks

    Chris
  3. UI for ASP.NET Ajax is Ready for VS 2017
  4. Answer
    Slav
    Admin
    Slav avatar
    1356 posts

    Posted 16 Apr 2013 Link to this post

    Hi Chris,

    The Persistence Framework will not be suitable in this case, because it does not handle dynamically
    created controls. Please check its documentation and demos for more information about its use cases.

    Instead you can use a storage that is persisted through postbacks, for example Cookies, in order to save information about the tabs and page views that you created so that you can re-create them on subsequent postback.

    Also, according to your description the RadDockLayout is located in the user controls that are loaded in the dynamically created page views. This is not a recommended setup, because the RadDockLayout relies on page lifecycle events that are fired before the PageViewCreated event (in which the user controls with the dock layout are added on the page). As a result you will most probably encounter problems will persisting the state of the docks. The suggested approach in this case is to wrap the RadMultiPage and the RadTabStrip in the RadDockLayout.

    All the best,
    Slav
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
  5. Chris
    Chris avatar
    58 posts
    Member since:
    Feb 2012

    Posted 19 Apr 2013 Link to this post

    Thanks for the advise Slav.

    Although I understand why I should get the RadDockLayout out of my dynamically loaded controls and have only one instance on my main page but it worked pretty well with a RadDockLayout embedded in each of my dynamically created RadPageView.

    I followed your advise though but now I'm struggling to know on which RadDockZone should I dock the newly created RadDock. Prioir to making the requested changes, that was easy since everything was handled from within my usercontrol, so a quick call to the method mydock.Dock(RadDockZone1) worked perfectly fine.

    Now When I click on my Add Dock button (that have moved from my PageView to my main page), I would like to attach this dock to the RadDockZone from the currently selected RadPageView. Should I do this in js ?

    Moreover, I've setup a RadColorPicker in each of my usercontrols that hold the RadDockZone. When changing the color from the colorpicker I'm trying to change the background color of my Docks from the current selected RadPageView. It kinda work except that no matter which RadColorPicker i'm using, it always changes the background color of the docks nested in the latest created RadDockZone.

    I know it's a long piece of code but here is my main page, managing the add of both Tabs and Docks:
    public partial class Default : System.Web.UI.Page
    {
        private static int _id = 0;
        private string tabName;
        private string _suffixID;
     
        private Dictionary<string, Line> _CurrentLines
        {
            get
            {
                Dictionary<string, Line> _ll = (Dictionary<string, Line>)Session["CurrentLines"];
                if (Object.Equals(_ll, null))
                {
                    _ll = new Dictionary<string, Line>();
                    Session["CurrentLines"] = _ll;
                }
                return _ll;
            }
            set
            {
                Session["CurrentLines"] = value;
            }
        }
     
        private List<DockState> CurrentDockStates
        {
            get
            {
                List<DockState> _currentDockStates = (List<DockState>)Session["CurrentDockStates"];
                if (Object.Equals(_currentDockStates, null))
                {
                    _currentDockStates = new List<DockState>();
                    Session["CurrentDockStates"] = _currentDockStates;
                }
                return _currentDockStates;
            }
            set
            {
                Session["CurrentDockStates"] = value;
            }
        }
     
        protected void Page_Load(object sender, EventArgs e)
        {
            _suffixID = this.ClientID.Replace("-", "").Replace("$", "");
        }
     
        protected void Page_Init(object sender, EventArgs e)
        {
            try
            {
                foreach (KeyValuePair<string, Line> line in _CurrentLines)
                {
                    RadTab tab = CreateRadTabFromSession(line.Key, line.Value);
                }
     
                for (int i = 0; i < CurrentDockStates.Count; i++)
                {
                    RadDock dock = CreateRadDockFromState(CurrentDockStates[i]);
                    RdlLineBuilder.Controls.Add(dock);
                    CreateSaveStateTrigger(dock);
                    if (CurrentDockStates[i].Closed == true)
                    {
                        dock.Visible = false;
                    }
                }
            }
            catch (Exception ex)
            {
     
                throw;
            }
        }
     
     
        #region DynamicDocks
     
        protected void RdlLineBuilder_LoadDockLayout(object sender, DockLayoutEventArgs e)
        {
            foreach (DockState state in CurrentDockStates)
            {
                e.Positions[state.UniqueName] = state.DockZoneID;
                e.Indices[state.UniqueName] = state.Index;
            }
        }
     
        protected void RdlLineBuilder_SaveDockLayout(object sender, DockLayoutEventArgs e)
        {
            CurrentDockStates = RdlLineBuilder.GetRegisteredDocksState();
        }
     
        private RadDock CreateRadDockFromState(DockState state)
        {
            RadDock dock = new RadDock();
            dock.DockMode = DockMode.Docked;
            dock.ID = string.Format("RadDock{0}", state.UniqueName);
            dock.ApplyState(state);
            dock.ContentTemplate = new LineStopContentTemplate("Stop");
            dock.DockHandle = DockHandle.None;
            dock.OnClientInitialize = "setHandle";

            return dock;
        }
     
        private RadDock CreateRadDock()
        {
            RadDock dock = new RadDock();
            dock.DockMode = DockMode.Docked;
            dock.UniqueName = Guid.NewGuid().ToString().Replace("-", "a");
            dock.ID = string.Format("RadDock{0}", dock.UniqueName);
            dock.Text = string.Format("Added at {0}\n{1}", DateTime.Now, dock.UniqueName);
            dock.ContentTemplate = new LineStopContentTemplate("Stop");
     
            dock.DockHandle = DockHandle.None;
            dock.OnClientInitialize = "setHandle";
            dock.Width = Unit.Pixel(120);
            dock.Height = Unit.Pixel(200);

            return dock;
        }
     
        private void CreateSaveStateTrigger(RadDock dock)
        {
            dock.AutoPostBack = true;
            dock.CommandsAutoPostBack = true;
        }
     
        protected void ButtonAddStop_Click(object sender, EventArgs e)
        {
            RadDock dock = CreateRadDock();
            RdlLineBuilder.Controls.Add(dock);
            //dock.Dock(RdzLineBuilder); // This is where I was docking my new dock when this was part of the usercontrol code behind
            
            CreateSaveStateTrigger(dock);
        }
     
        #endregion
     
        #region DynamicTabs
     
        private RadTab CreateRadTabFromSession(string id, Line line)
        {
            try
            {
                var tab = new RadTab(line.Name);
                rtsLines.Tabs.Add(tab);
                var pv = new RadPageView();
                pv.ID = "pv" + line.Name;
                pv.Height = Unit.Pixel(200);
                pv.Width = Unit.Pixel(800);
                mpLines.PageViews.Add(pv);
     
                return tab;
            }
            catch (Exception ex)
            {
                return null;
                throw;
            }
        }
     
        protected void rbtAddLine_Clicked(object sender, EventArgs e)
        {
            try
            {
                tabName = "New_" + _id++;
                var tab = new RadTab(tabName);
                rtsLines.Tabs.Add(tab);
                var pv = new RadPageView();
                pv.ID = "pv" + tabName;
                pv.Height = Unit.Pixel(200);
                pv.Width = Unit.Pixel(800);
                mpLines.PageViews.Add(pv);
                _CurrentLines.Add(tab.Text, new Line() { Name = tabName });
            }
            catch (Exception ex)
            {
                throw;
            }
        }
     
        protected void rtsLines_TabCreated(object sender, RadTabStripEventArgs e)
        {
            try
            {
                var closeButton = new HtmlGenericControl("div");
                closeButton.Attributes.Add("class", "btnCloseTab");
                closeButton.Attributes.Add("onclick", "closeTab('" + e.Tab.Text + "');");
                Label lbl = new Label();
                lbl.Text = e.Tab.Text;
                e.Tab.Controls.Add(closeButton);
                e.Tab.Controls.Add(lbl);
            }
            catch (Exception ex)
            {
                throw;
            }
        }
     
        protected void mpLines_PageViewCreated(object sender, RadMultiPageEventArgs e)
        {
            UserControl LineBuilder = (UserControl)this.LoadControl("usercontrol/LineBuilderV2.ascx");
            LineBuilder.ID = "LineBuilder_" + e.PageView.UniqueID;
            e.PageView.Controls.Add(LineBuilder);
        }
     
        #endregion
     
        protected void RadAjaxManager1_AjaxRequest(object sender, AjaxRequestEventArgs e)
        {
            try
            {
                var command = e.Argument.Split('|')[0];
                var args = e.Argument.Split('|').Length > 0 ? e.Argument.Split('|')[1] : null;
     
                switch (command)
                {
                    case "CloseTab":
                        var tab = rtsLines.FindTabByText(args, true);
                        var pv = tab.PageView;
                        rtsLines.Tabs.Remove(tab);
                        mpLines.PageViews.Remove(pv);
                        _CurrentLines.Remove(tab.Text);
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
     
                throw;
            }
        }
    }


    Here is the aspx:
    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="Default" %>
     
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <head runat="server">
        <title></title>
        <link href="css/lines.css" rel="stylesheet" type="text/css" />
        <link href="css/lineBuilder.css" rel="stylesheet" type="text/css" />
        <telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server" />
    </head>
    <body>
        <form id="form1" runat="server">
        <telerik:RadScriptManager ID="RadScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.Core.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQuery.js" />
                <asp:ScriptReference Assembly="Telerik.Web.UI" Name="Telerik.Web.UI.Common.jQueryInclude.js" />
            </Scripts>
        </telerik:RadScriptManager>

        <telerik:RadCodeBlock ID="rcbLineManager" runat="server">
     
            <script type="text/javascript">
                function closeTab(tabText) {
                    try {
                        var tabStrip = $find("<%= rtsLines.ClientID %>");
                        var multiPage = $find("<%= mpLines.ClientID %>");
                        var ajaxManager = $find("<%= RadAjaxManager1.ClientID %>");
                        var tab = tabStrip.findTabByText(tabText);
                        var pageView = tab.get_pageView();
     
                        var tabToSelect = tab.get_nextTab();
                        if (!tabToSelect)
                            tabToSelect = tab.get_previousTab();
     
                        tabStrip.get_tabs().remove(tab);
                        multiPage.get_pageViews().remove(pageView);
                        if (ajaxManager != null) {
                            ajaxManager.ajaxRequest("CloseTab|" + tabText);
                        } else {
                            console.log("ajaxManager is null");
                        }
     
                        if (tabToSelect)
                            tabToSelect.set_selected(true);
                    }
                    catch (err) {
                        console.log(err.message);
                    }
                }
            </script>
     
        </telerik:RadCodeBlock>
        <telerik:RadAjaxManager ID="RadAjaxManager1" runat="server" OnAjaxRequest="RadAjaxManager1_AjaxRequest">
            <AjaxSettings>
                <telerik:AjaxSetting AjaxControlID="rbtAddLine">
                    <UpdatedControls>
                        <telerik:AjaxUpdatedControl ControlID="rtsLines" />
                        <telerik:AjaxUpdatedControl ControlID="mpLines" LoadingPanelID="ralpLines" />
                    </UpdatedControls>
                </telerik:AjaxSetting>
            </AjaxSettings>
        </telerik:RadAjaxManager>
        <telerik:RadButton ID="rbtAddLine" runat="server" OnClick="rbtAddLine_Clicked" Text="New">
        </telerik:RadButton>
        <telerik:RadButton ID="ButtonAddStop" runat="server" OnClick="ButtonAddStop_Click" Text="Add Stop" />
        <div>
            <telerik:RadDockLayout ID="RdlLineBuilder" runat="server" OnSaveDockLayout="RdlLineBuilder_SaveDockLayout"
                OnLoadDockLayout="RdlLineBuilder_LoadDockLayout">
                <telerik:RadTabStrip ID="rtsLines" runat="server" CssClass="rtsLines" MultiPageID="mpLines"
                    OnTabCreated="rtsLines_TabCreated">
                </telerik:RadTabStrip>
                <telerik:RadMultiPage ID="mpLines" runat="server" CssClass="multiPageViewContent"
                    Height="400" Width="900" OnPageViewCreated="mpLines_PageViewCreated">
                </telerik:RadMultiPage>
                <telerik:RadAjaxLoadingPanel ID="ralpLines" runat="server">
                </telerik:RadAjaxLoadingPanel>
            </telerik:RadDockLayout>
        </div>
        </form>
    </body>
    </html>


    Now my usercontrol loaded in each RadPageView (there is nothing in the code behind):
    You can notice that I'm using 2 different approaches to setup the background color on 2 different events. Both are "working" but only for the latest RadDockZone created through this control.
    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="LineBuilderV2.ascx.cs"
        Inherits="TabsStrips.usercontrol.LineBuilderV2" %>
    <telerik:RadAjaxLoadingPanel ID="RalpLineBuilder" runat="server" MinDisplayTime="500">
    </telerik:RadAjaxLoadingPanel>
    <telerik:RadFormDecorator ID="RfdLineBuilder" runat="server" DecoratedControls="All"
        EnableRoundedCorners="true" />
    <telerik:RadScriptBlock ID="rsbLine" runat="server">
     
        <script type="text/javascript">
            function OnClientColorChange(sender, eventArgs) {
                var rdz = "<%= RdzLineBuilder.ClientID %>";
                console.log(rdz);
                $("#" + rdz + " .LBStop, " + "#" + rdz + " .LBLine").css('background-color', sender.get_selectedColor());
            }
     
            function OnClientColorPreview(sender, eventArgs) {
                try {
                    var rdz = $find("<%= RdzLineBuilder.ClientID %>");
                    var docks = rdz.get_docks();
                    for (cnt = 0; cnt < docks.length; cnt++) {
                        var elt = docks[cnt].get_contentContainer();
                        $(".LBStop, .LBLine", elt).css('background-color', eventArgs.get_color());
                    }
                }
                catch (err) {
                    console.log("--> " + err.message);
                }
            }
     
            function removeStop(name) {
                console.log('close dock :' + name);
            }
     
            function setHandle(dock, args) {
                var dockContent = dock.get_contentContainer();
                var dragHandle = $telerik.$(dockContent).find("[class='LBStop']")[0];
                dock.set_handle(dragHandle);
            }
        </script>
     
    </telerik:RadScriptBlock>
    <asp:Panel ID="PLineBuilder" runat="server" Width="800px">
            <telerik:RadDockZone ID="RdzLineBuilder" runat="server" Width="100%" Height="200px"
                Orientation="Horizontal">
            </telerik:RadDockZone>
        <telerik:RadColorPicker ID="colorpicker" runat="server" ShowIcon="true" OnClientColorChange="OnClientColorChange"
            OnClientColorPreview="OnClientColorPreview" PaletteModes="HSV">
        </telerik:RadColorPicker>
    </asp:Panel>


    My DockContent Template:
    public class LineStopContentTemplate : ITemplate
    {
        private string _name;
        private LineStopDesigner _lsd;
     
        public LineStopContentTemplate(string name)
        {
            _name = name;
            _lsd = new LineStopDesigner();
            _lsd.Name = name;
        }
     
        public void InstantiateIn(Control container)
        {
            container.Controls.Add(_lsd);
        }
     
    }

    My LineStopDesigner class:
    [DefaultProperty("Name")]
    [ToolboxData("<{0}:LineStopDesigner runat=server></{0}:LineStopDesigner>")]
    public class LineStopDesigner : WebControl
    {
        [Bindable(true)]
        [Category("Appearance")]
        [DefaultValue("")]
        [Localizable(true)]
        public string Name
        {
            get
            {
                String s = (String)ViewState["Name"];
                return ((s == null) ? String.Empty : s);
            }
     
            set
            {
                ViewState["Name"] = value;
            }
        }
     
        protected override void RenderContents(HtmlTextWriter output)
        {
            var container = new HtmlGenericControl("div");
            container.Attributes.Add("class", "LBStopContainer");
     
            var closeButton = new HtmlGenericControl("div");
            closeButton.Attributes.Add("class", "btnRemoveStop");
            closeButton.Attributes.Add("onclick", "removeStop('" + Name + "');"); 
            var line = new HtmlGenericControl("div");
            line.Attributes.Add("class", "LBLine");
            var stop = new HtmlGenericControl("div");
            stop.Attributes.Add("class", "LBStop");
            var stopName = new HtmlGenericControl("div");
            stopName.Attributes.Add("class", "LBStopName");
            stopName.InnerText = Name;
            container.Controls.Add(closeButton);
            container.Controls.Add(line);
            container.Controls.Add(stop);
            container.Controls.Add(stopName);
            container.RenderControl(output);
        }
    }


    Would someone be able to tell me what is going on there ? Slav ? Do you have a clue ?
  6. Answer
    Slav
    Admin
    Slav avatar
    1356 posts

    Posted 23 Apr 2013 Link to this post

    Hello Chris,

    You can locate the RadDockZone in the user control that is located in the content of the currently active RadPageView and pass its object as a parameter of the Dock method of RadDock. The index of the selected page view can be retrieved via the property SelectedIndex of RadTabStrip.

    As for your problem with setting the background color, if you are using the same names for the JavaScript functions in your user controls, the last one that is added on the page will override the others. The solution in this case is to use unique function names in every user control.

    Greetings,
    Slav
    the Telerik team
    If you want to get updates on new releases, tips and tricks and sneak peeks at our product labs directly from the developers working on the RadControls for ASP.NET AJAX, subscribe to their blog feed now.
  7. Chris
    Chris avatar
    58 posts
    Member since:
    Feb 2012

    Posted 23 Apr 2013 Link to this post

    Thanks for your reply Slav.
    And sorry for that dumb mistake of having the same js function name in my UC. I realized that the other day and fixed it since.
    I have other questions but I think I'll close this ticket and create a new one if I can't find the answer in the previously asked questions.

    Many thanks for your support, your Team and Products are awesome ;)

    Cheers
Back to Top
UI for ASP.NET Ajax is Ready for VS 2017