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.
Here's the code behind:
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
Here's the code behind:
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
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">
<
html
xmlns
=
"http://www.w3.org/1999/xhtml"
>
<
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(
this
,
this
.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