Lazy loading tabs with user controls - c#

I want to use lazy loading of tabs in AJAX tab container. I have implemented it. But the problem that I am facing is that when I click a button or fire any event in that user control, it is not fired; nothing happens.
<asp:TabContainer runat="server" ID="TabContainerUp"
ActiveTabIndex="0" AutoPostBack="true" OnActiveTabChanged="TabContainerUp_ActiveTabChanged">
<asp:TabPanel ID="tab1" runat="server">
<HeaderTemplate>
<img src="images/uc1.png" alt="" />
</HeaderTemplate>
<ContentTemplate>
<asp:Panel ID="pnlUC1" runat="server">
</asp:Panel>
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="tab2" runat="server">
<HeaderTemplate>
<img src="images/uc2.png" alt="" />
</HeaderTemplate>
<ContentTemplate>
<asp:Panel ID="pnlUC2" runat="server">
</asp:Panel>
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
codebehind:
protected void TabContainerUp_ActiveTabChanged(object sender, EventArgs e)
{
string tabName = TabContainerUp.ActiveTab.ID;
getActiveTab(tabName);
}
public void getActiveTab(string tabName)
{
UserControl uc;
//uc.
switch (tabName)
{
case "tab1":
pnlUC1.Controls.Clear();
uc = Page.LoadControl("~/Controls/UC1.ascx") as UserControl;
pnlUC1.Controls.Add(uc);
break;
case "tab2":
pnlUC2.Controls.Clear();
uc = Page.LoadControl("~/Controls/UC1.ascx") as UserControl;
pnlUC2.Controls.Add(uc);
break;
}
}

You need to recreate dynamically created controls on every postback in Page_Load at the latest, with the same ID as before. So you can load and add them to your panels in ActiveTabChanged, but you need to recreate them in the next postback in Page_Init/Page_Load. Therefor you need to store somewhere what to recreate (f.e. in Session).
But i assume that you're making things more complicated than necessary, you could simply create these UserControls even declaratively (on aspx) with an initial Visible state of false. Then you only need to switch visibility of the controls as necessary in ActiveTabChanged.
Note: invisible serverside webcontrols will not be rendered at all to the client and no ViewState will be saved. So there's no disadvantage in declaring them.
Lazy-Load does not mean that you create these controls as late as possible but it means that you databind them as late as possible. So never bind them to the database from page_load (f.e. in the UserControl), but only from methods that will be called when necessary from the page(here from ActiveTabChanged). Therefor you could implement a public method BindData in your UserControl UC1.
Here's a simple example:
switch (tabName)
{
case "tab1":
UC1_1.Visible = true;
UC1_1.BindData();
UC1_2.Visible = false;
break;
case "tab2":
UC1_1.Visible = false;
UC1_2.Visible = true;
UC1_2.BindData();
break;
}
and in your UserControl
public void BindData()
{
// put here all your databinding stuff
// that is in page_load now ...
}
This is probably the best tutorial on lazy-loading ajax TabPanels:
http://mattberseth.com/blog/2007/07/how_to_lazyload_tabpanels_with.html

Related

ASP updatepanel doesn't refresh ajax tabpage

I have an ajax tabcontainer in an updatepanel with all tabpages set visible until you want to add a tabpanel based on the dropdownlist selected value
CODE:
<cc1:TabContainer ID="tabControlParameters" runat="server" CssClass="ajax__tab_xp"
ScrollBars="Both" ActiveTabIndex="15" UseVerticalStripPlacement="True">
<%--EnvironmentTab --%>
<cc1:TabPanel ID="pnlEnvironment" HeaderText="Environment" runat="server" Visible="false">
<ContentTemplate>
//somecontent
</ContentTemplate>
</cc1:TabPanel>
<cc1:TabPanel ID="pnlDatabase" HeaderText="Environment" runat="server" Visible="false">
<ContentTemplate>
//somecontent
</ContentTemplate>
</cc1:TabPanel>
<cc1:TabPanel ID="pnlFirstError" HeaderText="Environment" runat="server" Visible="false">
<ContentTemplate>
//somecontent
</ContentTemplate>
</cc1:TabPanel>
With a button add which is inside the Updatepanel and has a correct async trigger assigned to it.
From C# codebehind I've made a loop to check if the dropdownlist selectedvalue = panel_headertext if so make it visible
CODE:
protected void btnAddParameters_Click(object sender, EventArgs e)
{
String Parameter = ddlParameterTypes.SelectedValue.ToString();
AjaxControlToolkit.TabContainer container = (AjaxControlToolkit.TabContainer)tabControlParameters;
foreach (object obj in container.Controls)
{
if (obj is AjaxControlToolkit.TabPanel)
{
AjaxControlToolkit.TabPanel tabPanel = (AjaxControlToolkit.TabPanel)obj;
if (tabPanel.HeaderText == ddlParameterTypes.SelectedValue)
{
tabPanel.Visible = true;
tabPanel = tabControlParameters.ActiveTab;
container.ActiveTab = tabPanel;
}
}
}
}
Now this works perfectly if the updatepanel trigger is set to fullPostback but it's set to async postback then it only works on the first click even though the event is fired every time I'm clicking on the button. Am I missing something obvious here?
Petar
You have the same value in HeaderText for each of your TabPanels. I think it'll work if you correct the HeaderText attributes.

How to hide and display asp:buttons in asp.net from code behind?

I am working on asp.net web application.
In one Page I have two asp buttons.
I want to display them in one condition otherwise I don't want to display them.
So I'm trying to do the same like this. But Its not working.
I can't find the reason behind it. Please tell me where is the issue.
To Hide Buttons
if (!IsPostBack)
{
ButtonReplaceId.Style.Add("display", "none");
ButtonAssociateRules.Style.Add("display", "none");
}
To display buttons
protected void ApplyAssociation(object sender, EventArgs e)
{
//Some piece of code
if(a==0)
{
ButtonAssociateRules.Style.Add("display", "block");
ButtonReplaceId.Style.Add("display", "block");
}
}
aspx for buttons
<div style ="padding-left:400px;">
<asp:Button ID="ButtonAssociateRules" runat="server" OnClick="AssociateMultipleRulesButtonClick"
CssClass="search_button_in_vm_intersection" Text="Associate Multiple Rules"
OnClientClick="return OnClientClickAssociateRewardRuleFile();" />
<asp:Button ID="ButtonReplaceId" runat="server" OnClick="ApplyReplaceIfRuleIntersects"
CssClass="search_button_in_vm_intersection" Text="Replace Previous Rules"
OnClientClick="return OnClientClickReplaceRewardRuleFile();" />
</div>
aspx of button for OnClick event ApplyAssociation()
<asp:UpdatePanel runat="server" UpdateMode="Conditional">
<Triggers>
<asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" />
</Triggers>
<ContentTemplate>
<asp:Table runat="server" CssClass="rule_file_whole" BorderWidth="0" Style="padding-top: 30px;">
<asp:TableRow ID="MerchantRowAssociation" HorizontalAlign="Center">
<asp:TableCell>
<div style="text-align: center">
<asp:Button ID="AssociationMerchant" Text="Apply Association" runat="server" OnClick="ApplyAssociation"
CssClass="search_button_in_vm_associate1 " OnClientClick="return checkValidation()" />
</div>
</asp:TableCell>
</asp:TableRow>
</asp:Table>
</ContentTemplate>
</asp:UpdatePanel>
Seeing as you are using a conditional update panel, you can try either of these after putting the buttons inside an update panel.
protected void ApplyAssociation(object sender, EventArgs e)
{
//Some piece of code
if (a == 0)
{
ButtonAssociateRules.Style["visibility"] = "hidden";
ButtonReplaceId.Style["visibility"] = "hidden";
myUpdatePanel.Update();
}
}
protected void ApplyAssociation(object sender, EventArgs e)
{
//Some piece of code
if (a == 0)
{
ButtonAssociateRules.Visible = false;
ButtonReplaceId.Visible = false;
myUpdatePanel.Update();
}
}
Here's an example of your buttons inside an update panel.
<asp:UpdatePanel ID="myUpdatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<div style="padding-left:400px;">
<asp:Button ID="ButtonAssociateRules" runat="server" OnClick="AssociateMultipleRulesButtonClick"
CssClass="search_button_in_vm_intersection" Text="Associate Multiple Rules"
OnClientClick="return OnClientClickAssociateRewardRuleFile();" />
<asp:Button ID="ButtonReplaceId" runat="server" OnClick="ApplyReplaceIfRuleIntersects"
CssClass="search_button_in_vm_intersection" Text="Replace Previous Rules"
OnClientClick="return OnClientClickReplaceRewardRuleFile();" />
</div>
</ContentTemplate>
</asp:UpdatePanel>
You can simple use the Visible property of Button which is more straight forward and clean.
ButtonReplaceId.Visible = false;
If this property is false, the server control is not rendered. You
should take this into account when organizing the layout of your page.
If a container control is not rendered, any controls that it contains
will not be rendered even if you set the Visible property of an
individual control to true. In that case, the individual control
returns false for the Visible property even if you have explicitly set
it to true. (That is, if the Visible property of the parent control is
set to false, the child control inherits that setting and the setting
takes precedence over any local setting.) MSDN.
You are trying to change the state of control in ajax call that is not in current UpdatePanel. Put the buttons in the same UpdatePanel then you will be able to change the state.
ButtonReplaceId.Visible = false;
ButtonAssociateRules.Visible = false;

How to store multiple scroll positions for scrolling div element depending on active View in MultiView (asp.net webform

confusing title but the best way I can put it.
Basically I am currently using a single div with overflow:auto that contains different GridViews. The GridViews are swapped by using a MultiView with each indiviudal view containing a single GridView.
I would like to be able to store the scroll position of each view so that I can set the div's scroll position depending on the view that will be switched to.
Here is how my page is set up.
<div id="scrollingDiv" style="height:100%; overflow:auto;">
<div id="gridWrap">
<asp:UpdatePanel ID="UpdatePanel1" runat="server" RenderMode="Inline">
<ContentTemplate>
<asp:MultiView ID="MultiView1" runat="server">
<asp:View ID="view1" runat="server">
<asp:GridView ID="gridView1" runat="server">
</asp:GridView>
</asp:View>
<asp:View ID="view2" runat="server">
<asp:GridView ID="gridView2" runat="server">
</asp:GridView>
</asp:View>
</asp:Multiview>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</div>
So scrollingDiv will contain all the Views and will scroll for each one of the GridViews.
To switch between views I have a drop down connected to an
protected void DropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
switch (DownList.SelectedItem.Value)
{
case "view1":
MultiView1.SetActiveView(view1);
break;
case "view2":
MultiView1.SetActiveView(view2);
break;
}
}
I have been looking around and can't quite find something specific to my case. I would like to be able to use just the one overflow div but would understand if I had to make a separate overflow div for each view.
Any help would be great,
Thanks.
jQuery and some hidden fields can help. Try this...
<asp:Hiddenfield ID="hdnCurrentView" runat="server"/>
<asp:Hiddenfield ID="hdnMultiView1Top" runat="server"/>
<asp:Hiddenfield ID="hdnMultiView2Top" runat="server"/>
On backend, i.e., in your CS file set the current view in hidden field...
protected void DropDownList_SelectedIndexChanged(object sender, EventArgs e)
{
switch (DownList.SelectedItem.Value)
{
case "view1":
MultiView1.SetActiveView(view1);
hdnCurrentView.Value = "View1";
break;
case "view2":
MultiView1.SetActiveView(view2);
hdnCurrentView.Value = "View2";
break;
}
}
Then in your jQuery ready function,
$().ready(function() {
//for storing scroll position of each view in respective hiddenfields
$(window).scroll(function () {
if ($("#hdnCurrentView").val() == "View1") {
$("#hdnMultiView1Top").val($(window).scrollTop());
}
else if ($("#hdnCurrentView").val() == "View2") {
$("#hdnMultiView2Top").val($(window).scrollTop());
}
});
//for restoring scroll position on page load i.e., ready function in jQuery
if ($("#hdnCurrentView").val() == "View1") {
$(window).scrollTop($("#hdnMultiView1Top").val());
}
else if ($("#hdnCurrentView").val() == "View2") {
$(window).scrollTop($("#hdnMultiView2Top").val());
}
});
This would maintain the vertical scroll position for each multiview. If you want to maintain horizontal scroll positions too, have hidden fields for each view & use jQuery function given below. Left to you as an excercise...
$(window).scrollLeft(value);
Happy Coding...;)

Cloning AJAX TabPanels

I have come across an interesting problem when trying to clone a template panel in my ajax tabcontainer control.
The idea is that i have a custom control on the first tab that lists some things, and to add a new thing you click the new button on the custom control, which raises an event in the control / page that contains the tabcontainer. That control / page then goes about cloning the hidden tabpanel and adding the clone to the tabcontainer.
with this markup I get what I need from both the first tab (containing the list) and any subsequent tabs (templated by the hidden tabpanel ready for cloning) ...
<asp:TabContainer ID="TabContainer1" runat="server">
<asp:TabPanel ID="ui_pnl1" HeaderText="My Panel" runat="server">
<ContentTemplate>
<cc1:myListOfThings ID="list" runat="server" OnMyEvent="CreateTabFromTemplate" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="TemplatePanel" runat="server" Visible="false">
<HeaderTemplate>
<span>Hello World</span><asp:LinkButton ID="ui_btnRemove" runat="server" Text="x" />
</HeaderTemplate>
<ContentTemplate>
Some content for my panel
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
Ok now lets assume that on the first panel in my custom control i have a button that raises the "MyEvent" event which in turn calls the method "CreateTabFromTemplate".
Now what I want to do is copy the hidden panel "TemplatePanel" and add it to the tab container.
In my code behind, the method code for adding the new tab panel to my tab container works something like this ...
protected void CreateTabFromTemplate(object sender, EventArgs e)
{
// create a new tab panel
TabPanel newPanel = new TabPanel();
// instantiate the hidden content template from the hidden note panel in the new panel
ui_tpNoteCreator.ContentTemplate.InstantiateIn(newPanel);
// add the panel to the available tabs and select it
TabContainer1.Tabs.Add(newPanel);
TabContainer1.ActiveTab = newPanel;
}
All looking good so far ... but i missed something ... I haven't templated the new tabpanels header ... it seems that all I can do is set the text.
Following this example : http://forums.asp.net/t/1108611.aspx/1 I can do what i'm trying to do but I don't want to write a class that defines my header template I want to instantiate an instance of my markup version and pass that instance to my new panel.
I'm not convinced this can be done ... is this a bug with the control or did i miss something ?!?!
Any ideas?
It turns out i was going about it the wrong way ...
Essentially theres a difference between assigning templates and the databinding process, it's still not perfect because of the data im trying to pass in to my tab templates but here's the basic principal ...
Markup :
<asp:TabContainer ID="TabContainer1" runat="server">
<asp:TabPanel ID="ui_pnl1" HeaderText="My Panel" runat="server">
<ContentTemplate>
<cc1:myListOfThings ID="list" runat="server" OnMyEvent="CreateTabFromTemplate" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="TemplatePanel" runat="server" Visible="false">
<HeaderTemplate>
<span>Hello World</span><asp:LinkButton ID="ui_btnRemove" runat="server" Text="x" />
</HeaderTemplate>
<ContentTemplate>
Some content for my panel
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
Code behind :
protected void CreateTabFromTemplate(object sender, EventArgs e)
{
// create a new tab panel
TabPanel newPanel = new TabPanel();
newPanel.HeaderTemplate = TemplatePanel.HeaderTemplate;
newPanel.ContentTemplate = TemplatePanel.ContentTemplate;
// add the panel to the available tabs and select it
TabContainer1.Tabs.Add(newPanel);
TabContainer1.ActiveTab = newPanel;
}
protected void TabContainer_DataBinding(object sender, EventArgs e)
{
foreach(TabPanel panel in TabContainer.Tabs)
{
//identify if this is the correct tab
if(correctTab)
{
// this will find a control anywhere on the panel (eg in both header and content templates)
Label label = panel.FindControl("ControlID") as Label;
label.Text = "Some Business Object Value";
}
}
}
I just tested the following which works as far as I can tell.
Markup:
<asp:TabContainer ID="TabContainer1" runat="server" ViewStateMode="Enabled">
<asp:TabPanel ID="ui_pnl1" HeaderText="My Panel" runat="server">
<ContentTemplate>
<asp:Button ID="btnAddPanel" runat="server" Text="Add Panel" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="TemplatePanel" runat="server" Visible ="false">
<HeaderTemplate>
<span>Hello World</span><asp:LinkButton ID="ui_btnRemove" runat="server" Text="X" />
</HeaderTemplate>
<ContentTemplate>
<p>Test Content</p>
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
Code behind:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.btnAddPanel.Click += new EventHandler(btnAddPanel_Click);
}
void btnAddPanel_Click(object sender, EventArgs e)
{
TabPanel newPanel = new TabPanel();
newPanel.HeaderTemplate = TemplatePanel.HeaderTemplate;
TemplatePanel.ContentTemplate.InstantiateIn(newPanel);
TabContainer1.Tabs.Add(newPanel);
TabContainer1.ActiveTab = newPanel;
}
}

how to make a Image-button in the user control execute code in the defaultpage.aspx?

1-I have three user controls.
2-I added them to AJAX TabContainer on my default.aspx page
<asp:TabContainer ID="TabContainer1" runat="server">
<asp:TabPanel runat="server" ID="GroupOne">
<HeaderTemplate>
1
</HeaderTemplate>
<ContentTemplate>
<SUR:GroupOne ID="group1" runat="server" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="GroupTwo" runat="server">
<HeaderTemplate>
2
</HeaderTemplate>
<ContentTemplate>
<SUR:GroupTwo is="group2" runat="server" />
</ContentTemplate>
</asp:TabPanel>
<asp:TabPanel ID="GroupThree" runat="server">
<HeaderTemplate>
3
</HeaderTemplate>
<ContentTemplate>
<SUR:GroupThree ID="grup3" runat="server" />
</ContentTemplate>
</asp:TabPanel>
</asp:TabContainer>
3- in the first user control i have image-button
<asp:ImageButton ID="ImageButton1" runat="server" />
4- I have this code in my default.vb
Public Sub movit()
GroupThree.Enabled = True
TabContainer1.ActiveTab = GroupThree
End Sub
so how can i execute that sub when i click on the image button in the user-control??
I believe you need to use a delegate to achive this. You can try followings
In your ascx.cs or vb file Add a delegate inside the namespace but outside the UserControl class.
public delegate void ImageButtonClickEventHandler(Object sender, EventArgs args);
Inside UserControl class add an event using delegate.After that call the delegate inside ImageButtonClick event.
public event ImageButtonClickEventHandler ImageButtonClickEvent;
private void imageButton_Click(object sender, System.EventArgs e)
{
if(ImageButtonClickEvent!= null)
{
ImageButtonClickEvent(sender,e);
}
}
In your aspx page add this inside page load event.
UserControl1.ImageButtonClickEvent+=new ImageButtonClickEventHandler(UserControl1_ImageButtonClickEvent);
Finally declare UserControl1_ImageButtonClickEvent function
private void UserControl1_ImageButtonClickEvent(Object sender, EventArgs args)
{
//Call your methods
}
From your designer, double-click the button, this will wire up an event-handler (ImageButton1_Click) and set the onclick property of your imagebutton to ImageButton1_Click.
In the code behind of your page, just call movit() from the generated eventhandler.
[Edit]
Didn't see that the image button was in a user control.
The way to go would be to add an Event to your user control. Raise it when the user clicks the imagebutton. In the page, handle the new event of the user control and call the movit() function.
What aboiut..
1) add an event handler to the user control
public event EventHandler Click
{
add
{
ImageButton1.Click += value;
}
remove
{
ImageButton1.Click -= value;
}
}
2) subscribe to this event from the page you are using the control - default.aspx
<user:control runat="server" ID="uc" OnClick="uc_OnClick" />
and
protected void uc_OnClick(object sender, EventArgs e)
{
movit();
}

Categories