Trigger for UpdatePanel disappearing when added to a "custom control" - c#

Basically, in a nutshell, the problem is that dynamically generated triggers for an UpdatePanel can no longer be found (by ASP.NET) as soon as I add them as children of a custom control.
Since the amount of code I'm working on is quite substantial I've recreated the problem on a smaller scale, which will make it easier to debug.
The error thrown in my face is:
A control with ID 'theTrigger' could not be found for the trigger in UpdatePanel 'updatePanel'.
I'm not sure whether this implementation of a "custom control" is the right way to go about it, but I did not write the original implementation: I'm working with code written by a previous developer to which I cannot make large modifications. It looks a little unusual to me, but, alas, this is what I've been given.
Default.aspx
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="TestWeb.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>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Panel runat="server" ID="panel">
</asp:Panel>
<asp:ScriptManager ID="scriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel runat="server" ID="updatePanel" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="lblSomething" runat="server"></asp:Label>
</ContentTemplate>
</asp:UpdatePanel>
</div>
</form>
</body>
</html>
Default.aspx.cs
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace TestWeb
{
public partial class Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
UselessTableWrapper table = new UselessTableWrapper();
TableRow tr = new TableRow();
TableCell td = new TableCell();
LinkButton button1 = new LinkButton { ID = "theTrigger", Text = "Click Me" };
button1.Click += button1_Click;
td.Controls.Add(button1);
tr.Controls.Add(td);
table.AddRow(tr);
panel.Controls.Add(table);
// ### uncomment these lines (and comment the one above) to see it working
// ### without the custom control
/*
Table realTable = new Table();
realTable.Controls.Add(tr);
panel.Controls.Add(realTable);
*/
updatePanel.Triggers.Add(new AsyncPostBackTrigger { ControlID = "theTrigger", EventName = "Click" });
scriptManager.RegisterAsyncPostBackControl(button1);
}
protected void button1_Click(object sender, EventArgs e)
{
lblSomething.Text = "Random number: " + new Random().Next(100);
updatePanel.Update();
}
}
}
MyControl.cs
using System;
using System.Web.UI.WebControls;
namespace TestWeb
{
public class UselessTableWrapper : WebControl
{
private Table table = new Table();
protected override void OnPreRender(EventArgs e)
{
Controls.Add(table);
}
public void AddRow(TableRow row)
{
table.Controls.Add(row);
}
}
}
Any ideas would be greatly appreciated.
Edit
I've tried switching the OnPreRender event for this (found in a tutorial):
protected override void RenderContents(HtmlTextWriter writer)
{
writer.BeginRender();
table.RenderControl(writer);
writer.EndRender();
base.RenderContents(writer);
}
... hoping that it would fix it, but it does not.

this is the approach that I've taken with loading a ascx web control inside an aspx control from the code behind.
In the control:
namespace dk_admin_site.Calculations
{
public partial class AssignedFieldCalculation : System.Web.UI.UserControl
{
public static AssignedFieldCalculation LoadControl(Calculation initialData)
{
var myControl = (AssignedFieldCalculation) ((Page) HttpContext.Current.Handler).LoadControl(#"~\\Calculations\AssignedFieldCalculation.ascx");
myControl._initialData = initialData;
return myControl;
}
private Calculation _initialData;
public Calculation Data { get { return _initialData; } }
protected void Page_Load(object sender, EventArgs e) {}
}
}
in the web form code behind:
protected void Page_Load(object sender, EventArgs e)
{
if (this.IsPostBack)
{
if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnRemoveCalculationFromField"))
{
//do something on the postback
}
else if (ScriptManager1.AsyncPostBackSourceElementID.StartsWith("ctl00$MainContent$calc") && (ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationUp") || ScriptManager1.AsyncPostBackSourceElementID.EndsWith("$btnMoveCalculationDown")))
{
//do something on the postback
}
}
foreach (Calculation calc in calculationCollection)
{
AssignedFieldCalculation asCalc = AssignedFieldCalculation.LoadControl(calc);
asCalc.ID = "calc" + calc.UniqueXKey;
pnlFieldCalculations.Controls.Add(asCalc);
foreach (Control ct in asCalc.Controls)
{
if (ct.ID == "btnMoveCalculationDown" || ct.ID == "btnMoveCalculationUp" || ct.ID == "btnRemoveCalculationFromField")
{
ScriptManager1.RegisterAsyncPostBackControl(ct);
}
}
}
}
A few things to note:
You need to make each control ID unique when adding it to the asp:Panel (called pnlFieldCalculations).
The LoadControl method allows you to pass initial arguments

Related

Don't know how to use custom controls in code behind

I made a simple control with 1 text box.
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="sTextBox.ascx.cs" Inherits="TestingASPNET.Controls.sTextBox" className="sTextBox"%>
<asp:Textbox runat="server" ID="tbNothing"/>
<br />
I call this control as a reference in my default.aspx Here's the simple code.
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="TestingASPNET._default" %>
<%# Reference Control="~/Controls/sTextBox.ascx"%>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder runat="server" id="PlaceHolder1" />
</div>
</form>
</body>
</html>
In my code behind in default.aspx.cs I have.
protected void Page_Load(object sender, EventArgs e)
{
PlaceHolder1.Controls.Add(LoadControl("~/Controls/sTextBox.ascx"));
PlaceHolder1.Controls.Add(LoadControl("~/Controls/sTextBox.ascx"));
}
This adds the 2 sTextBox onto my page.
The problem I'm having is how to I use the control like I would a normal textBox. For example.
TextBox tb = new TextBox();
tb.Text = "textbox";
PlaceHolder1.Controls.Add(tb);
This adds a text box on the page with the text "textbox" in it.
Can Someone give me a way to do EXACTLY this, but with the control sTextBox.
you can get that behavior by adding properties to your custom control.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
var ctrl = (sTextBox) Page.LoadControl("~/sTextBox.ascx");
ctrl.Text = "something";
placeHolder1.Controls.Add(ctrl);
}
}
User Control :-
public partial class sTextBox : System.Web.UI.UserControl
{
public string Text { get; set; }
}
I couldn't get your code to work.
I either had to
var ctrl = (ProjectName.Controls.sTextBox) Page.LoadControl("~/Controls/sTextBox.ascx");
or import the control
using ProjectName.Controls;
When I did this, it worked.
Also your get set property wasn't work either, I had to change it to.
public string Text {
get
{
return tbNothing.Text;
}
set
{
tbNothing.Text = value;
}
}
Afterwards I added 1 more textbox into the control totaling 2. I changed the ID to tb1Text and tb2Text. I then had to get 2 methods for my get sets, which was
public string tb1Text {
get
{
return tb1.Text;
}
set
{
tb1.Text = value;
}
}
public string tb2Text
{
get
{
return tb2.Text;
}
set
{
tb2.Text = value;
}
}
inside my default code behind, I had to use
sTextBox ctrl = (sTextBox)Page.LoadControl("~/Controls/sTextBox.ascx");
ctrl.tb1Text = "something";
ctrl.tb2Text = "something 2";
PlaceHolder1.Controls.Add(ctrl);
This worked, now I know how to use 2 textboxes on 1 control :) . Hopefully it's the same with other controls that I have to make :S

Prevent duplication in asp:table on refresh using AJAX timer

I've created a very simple bit of code in C# to display data in an asp:Table. On the initial page load all looks great. I've used an AJAX timer to refresh every 15 seconds to keep the data up to date. The problem is after it refreshes the table shows all the data duplicated. I've tried debugging and again all looks good until the AJAX refresh and then it seems to jump all over the place in the C# code rather than following the expected sequence.
Hopefully I've not approached this in completely the wrong way but if I have please tell me! Is there a way to prevent/fix this duplication?
I've replicated the problem code here in a very simple form. Default.aspx:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="default.aspx.cs" Inherits="testASPTables._default" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:Table ID="Table1" runat="server">
</asp:Table>
<asp:Timer ID="Timer1" runat="server" Interval="15000" OnTick="Timer1_Tick">
</asp:Timer>
</div>
</form>
</body>
</html>
...and the codebehind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace testASPTables
{
public partial class _default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TableRow[] tRows = new TableRow[5];
TableCell[] tCells = new TableCell[5];
for (int i = 0; i < 5; i++)
{
tRows[i] = new TableRow();
Table1.Rows.Add(tRows[i]);
tCells[i] = new TableCell();
tRows[i].Cells.Add(tCells[i]);
tCells[i].Text = "Cell Number: " + i.ToString();
}
}
protected void Timer1_Tick(object sender, EventArgs e)
{
Page_Load(sender, e);
}
}
}
You're calling Page_Load twice. Page_Load runs on postback, then you call it again from Timer1_Click.
You need to always be concious of whether the page is in a postback or not and how Page_Load works.
Here's what I'd do.
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
LoadTableData();
}
}
protected void LoadTableData()
{
Table1.Rows.Clear(); //I added this
TableRow[] tRows = new TableRow[5];
TableCell[] tCells = new TableCell[5];
for (int i = 0; i < 5; i++)
{
tRows[i] = new TableRow();
Table1.Rows.Add(tRows[i]);
tCells[i] = new TableCell();
tRows[i].Cells.Add(tCells[i]);
tCells[i].Text = "Cell Number: " + i.ToString();
}
}
protected void Timer1_Tick(object sender, EventArgs e)
{
LoadTableData();
}
Oh, and in addition to that, you'd need to clear the existing table rows.
By the way, this is very inefficient. You're reloading the entire table every 15 seconds. Multiple that by a few clients and you're practically performing a DoS attack on yourself. Consider using SignalR to push new changes to the clients as rows are added to the underlying data.

Get value from my own web control

I write one example to create own control on ASP.NET Froms. The controls very simple- combobox and button. User need choose value and when after he submit the button, the value from combobox need display in label.
So. Code of my Control:
public class MyControl:Control,IPostBackEventHandler
{
protected override void Render(HtmlTextWriter writer)
{
writer.AddAttribute("size","1");
writer.AddAttribute("ID","List2");
writer.AddAttribute("name", "ListYear");
writer.RenderBeginTag(HtmlTextWriterTag.Select);
for (int i = 1950; i < DateTime.Now.Year; i++)
{
writer.RenderBeginTag(HtmlTextWriterTag.Option);
writer.WriteEncodedText(i.ToString());
writer.RenderEndTag();
}
writer.RenderEndTag();
writer.AddAttribute("type","submit");
writer.AddAttribute("value","ClickMe");
writer.AddAttribute("name","BtnChange");
writer.RenderBeginTag(HtmlTextWriterTag.Input);
writer.RenderEndTag();
base.Render(writer);
}
public delegate void OnClickEventHandler(object sender, EventArgs args);
public event OnClickEventHandler Click;
public void RaisePostBackEvent(string eventArgument)
{
Click(this, new EventArgs());
}
}
The Page ASP:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="TestMyControl.aspx.cs" Inherits="Hello.TestMyControl" %>
<%# Register assembly="Hello" namespace="Hello" tagPrefix="MyContrl" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label ID="Label1" runat="server" Text="Label" Visible="False"></asp:Label>
<br />
<MyContrl:MyControl runat="server" OnClick="Unnamed1_OnClick" ID="Control1"></MyContrl:MyControl>
</div>
</form>
</body>
</html>
And in the end Event function:
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+Control1.????+" year";
}
What substitute for a question mark that take the value from the list?
P.S. Something strange is going on. Because when I click the button, the handler is not called, and I can not get into Unnamed1_OnClick
Since you have set the value on an attribute, to retrieve it you need to access Attributes property
Make your control inherit from HtmlControl
public class MyControl : HtmlControl, IPostBackEventHandler
{
...
On your page
<MyContrl:MyControl runat="server" OnClick="Unnamed1_OnClick" ID="Control1"></MyContrl:MyControl>
On your code
Label1.Text = Control1.Attributes["value"];
You can debug this line to see all available attributes
You would need to pass the name of the combobox and its the value of the text that is selected.
Like this:
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+ myCustomControl.SelectedItem.Value.ToString()
+ " year";
}
(Sorry. I misread the initial post and edited my code accordingly once I realized my mistake.)
Add select list to your user control with name="YourSelectList"
then in the click event handler
protected void Unnamed1_OnClick(object sender, EventArgs args)
{
Label1.Visible = true;
Label1.Text="You choose "+Control1.YourSelectList.SelectedValue.ToString()+" year";
}

Dynamic controls - Alternative to clear all control in the container

I'm doing some test with dynamic controls. Here the code:
ASPX page
<!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>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True"
onselectedindexchanged="DropDownList1_SelectedIndexChanged">
<asp:ListItem Value="0">Nothing</asp:ListItem>
<asp:ListItem Value="1">Two buttons</asp:ListItem>
</asp:DropDownList>
<asp:Panel ID="Panel1" runat="server">
</asp:Panel>
</div>
</form>
</body>
</html>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (ViewState["CreateDynamicButton"] != null)
{
CreateControls();
}
}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
CreateControls();
}
void Button_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
Response.Write("You clicked the button with ID " + b.ID);
}
private void CreateControls()
{
if (DropDownList1.SelectedValue.Equals("1"))
{
Panel1.Controls.Clear();
Button b1 = new Button();
b1.ID = "b1";
b1.Text = "Button 1";
Button b2 = new Button();
b2.ID = "b2";
b2.Text = "Button 2";
b1.Click += new EventHandler(Button_Click);
b2.Click += new EventHandler(Button_Click);
Panel1.Controls.Add(b1);
Panel1.Controls.Add(b2);
ViewState["CreateDynamicButton"] = true;
}
}
}
This code works but as you can see I remove all controls in the Panel1.Controls before add the buttons becouse when I choose to create them for the second time I get an exeption for duplicated controls ID.
I think that for two buttons the operation is very fast but with a larger number of controls the elaboration time will be longer.
Can you suggest me a better way to re-generate controls after PostBack without this workaround?
For starters, if you're trying to remove the controls (in this case buttons) for good, are you best off using the Dispose method? clearing the panel of the control won't dispose of it, which would explain the already in use ID's. You can do a simple loop to do this, regardless of how many controls are in the panel;
foreach(Control control in Container)
{
control.Dispose();
}
also, to create buttons i see you're just naming them btn1, btn2 and so on. You could do this in a loop too;
for(int i = 0; i >= yourInt; i++)
{
Button b = new Button();
b.ID = "b" + i;
b.Text = "Button " + i;
}
}
Create a class variable private bool ControlsCreated = false;
In your CreateControls method then check
if (!ControlsCreated) {
//your code to create controls
}
This makes sure the controls are created only once. If at any point later you need to recreate the controls (dropdownlist value changed), just clear the container and set ControlsCreated to false.

Viewstate is null on postback

Right, I've got something very peculiar going on here...
ASP.NET 4 page with the following property:
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
During the initial Page_Load() in (!Page.IsPostBack) the QuickShopBagInstance is populated and ViewState saved.
However when you perform a postback on the page the ViewState is empty when accessed from the postback Button_OnClick() event!!!
I've checked the Request.Form and sure enough the _Viewstate value is there and is populated. I've also ran this value through a parser and it does contain the expected data, the page has ViewStateEnabled="true" and the new .NET 4 ViewStateMode="Enabled".
I've moved on to override the LoadViewState method to check to see if it is firing, it doesn't appear to be.
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}
I am really lost as to what could possibly be the problem. Any ideas?
First of all I was mistaken, the code in question was not in Page_Load but in Page_Init, although I haven't read anything that says you can't assign to ViewState at Init.
So I put together a very basic test that duplicates the problems I'm having...
<form id="form1" runat="server">
<div>
<asp:ListView id="QuickshopListView" runat="server">
<LayoutTemplate>
<asp:PlaceHolder ID="itemPlaceHolder" runat="server" />
</LayoutTemplate>
<ItemTemplate>
<asp:TextBox ID="txtItem" runat="server" Text='<%# Container.DataItem %>' />
<asp:Button ID="btnDelete" runat="server" Text="Delete" OnClick="btnDelete_Click" />
<br />
</ItemTemplate>
</asp:ListView>
<asp:Button ID="btnAdd" runat="server" Text="Add" OnClick="btnAdd_Click" />
</div>
</form>
public partial class Quickshop : System.Web.UI.Page
{
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
protected void Page_Init(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
if (QuickShopBagInstance == null)
QuickShopBagInstance = new QuickShopBag();
if (!String.IsNullOrEmpty(Request.QueryString.ToString()))
{
string[] items = Server.UrlDecode(Request.QueryString.ToString()).Split(',');
if (items.Length > 0)
{
foreach (string item in items)
{
QuickShopBagInstance.QuickShopItems.Add(item);
}
}
}
}
}
protected void Page_LoadComplete(object sender, EventArgs e)
{
QuickshopListView.DataSource = QuickShopBagInstance.QuickShopItems;
QuickshopListView.DataBind();
}
protected void btnAdd_Click(object sender, EventArgs e)
{
if (QuickShopBagInstance == null)
QuickShopBagInstance = new QuickShopBag();
QuickShopBagInstance.QuickShopItems.Add("add1");
QuickShopBagInstance.QuickShopItems.Add("add2");
QuickShopBagInstance.QuickShopItems.Add("add3");
}
protected void btnDelete_Click(object sender, EventArgs e)
{
Button DeleteButton = (Button)sender;
ListViewDataItem item = (ListViewDataItem)DeleteButton.NamingContainer;
QuickShopBagInstance.QuickShopItems.RemoveAt(item.DisplayIndex);
}
}
[Serializable]
public class QuickShopBag
{
public List<string> QuickShopItems { get; set; }
public QuickShopBag()
{
this.QuickShopItems = new List<string>();
}
}
If you request say "/quickshop.aspx?add1,add2,add3", the ListView is populated correctly with the data from the qs, however when it comes to clicking the delete button a NullReferenceException is thrown because the ViewState hasn't persisted the QuickShopBag object.
But if you click the "Add" button, which as you can see adds to the same values to the QuickShopBagInstance (and ViewState), the ListView is populated correctly and when you click the Delete button it works perfectly as the ViewState has been persisted.
Now if you change the reading the querystring bit to Page_InitComplete as opposed to Page_Init it works perfectly. So the conclusion is...
YOU CAN'T ADD TO THE VIEWSTATE BEFORE Init_Complete!!!!!!!!
How silly of me, well whoever wrote it at least!
You seem to have ruled out most of the suggestions so far so I've created a basic page with only the information you've provided above:
class
namespace SO_Questions
{
[Serializable()]
public class QuickShopBag
{
public string MyProperty { get; set; }
}
}
code behind
namespace SO_Questions
{
public partial class TestPage : System.Web.UI.Page
{
protected QuickShopBag QuickShopBagInstance
{
get { return (QuickShopBag)ViewState["QuickShopBag"]; }
set { ViewState["QuickShopBag"] = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
this.QuickShopBagInstance = new QuickShopBag() { MyProperty = "Test String" };
}
Message.Text = "Value is: " + this.QuickShopBagInstance.MyProperty.ToString();
}
protected override void LoadViewState(object savedState)
{
base.LoadViewState(savedState);
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
btnSubmit.Text += QuickShopBagInstance.MyProperty;
}
}
}
markup:
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="TestPage.aspx.cs" Inherits="SO_Questions.TestPage" ViewStateMode="Enabled" %>
<!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></head>
<body>
<form id="form1" runat="server"><div>
<asp:Label ID="Message" runat="server"></asp:Label>
<asp:Button runat="server" ID="btnSubmit" Text="Submit" OnClick="btnSubmit_Click" />
</div></form>
</body></html>
As expected, this runs correctly; the overridden LoadViewState method is hit (and viewstate correctly contains 2 items) and the button's text is updated.
The logical explanation would be that there's something else going on somewhere else, or you've failed to provide an additional salient piece of information.
Something that has tripped me up in the past is
Setting something in the ViewState in the page
Then trying to retrieve it in a user control. Can't find it - where has it gone?
It seems as if you should have one ViewState per page but each usercontrol keeps it's own version.
Could it be something like this?
This SO link gives a better explanation that I have just done
Just a quick note. If you are setting a ViewState value on page_load, make sure you are doing it wrapped in
if (!IsPostBack)
{
ViewState["MyValue"] = MyValue // set dynamically with appropriate code
}
If you don't do this and you do a postback...but your code setting this ViewState value is not in the !IsPostBack brackets, it will reset your ViewState value to null every time you do a postback, no matter where you enable ViewState on the page.
This may seem obvious, but it is easy to miss if you have a lot of code going on.
Or I guess you could not use !IsPostBack if you want your code to run on every postback. But if you are having trouble with a value getting set to null on postback, examine the above carefully.

Categories

Resources