473,320 Members | 1,978 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Composite Control: Changes not being stored on postback

This involves a family of related, databound ASPNET2 composite controls.

I've managed to arrange things so that the composite controls restore themselves from ViewState on postback after they're initially
configured during DataBind(). Thanks to Steven Cheng for pointing out that you have to set the constituent control properties after
you add them to the composite control collection for the restore to work!

However, I now have a different problem. At least, I think it's a different problem.

One of the composite controls is a collection of HtmlInputRadioButton controls. All are initially unselected. When I select a
particular HtmlInputRadioButton and do a simple postback (i.e., just a roundtrip to the server; no other processing takes place) the
page that reappears doesn't show the selection. Instead, all the HtmlInputRadioButton controls in that composite control are
unselected again.

But here's the weird part: if I make the selection again -- or indeed any selection, from any of the other, related composite
controls on the same page -- and do a postback, the selection(s) show up! For some reason, the very first selections, whatever they
are, don't get stored. But the ones on subsequent roundtrips do.

I find this very confusing. I think this may mean there's something wrong with the way in which the controls are initialized when
first created (i.e., through DataBind()), but I'm not sure. Or does it mean that I have to do some postback handling? But if so, why
does the Framework appear to take care of things on the subsequent roundtrips?

Can anyone suggest some ideas on what may be causing the behavior? And how I fix it?

- Mark
Mar 1 '06 #1
4 4069
Hi Mark,

Welcome and glad to see that you're got progress on your custom controls.

Regarding on the new problem you mentioned, I think at least the readio
buttions have correct restore their status from viewstate and updates are
stored since in the sequential postback, the change has been displayed. The
problem is possibly that some other code(at creating time or intializing
time) override the control setting. Are you still use databinding or just
a collection to dynamically create the Html radio button list? If
convenient, would you provide some furhter code logic so that I can try
performing some local test.

Regards,

Steven Cheng
Microsoft Online Support

Get Secure! www.microsoft.com/security
(This posting is provided "AS IS", with no warranties, and confers no
rights.)
Mar 1 '06 #2
Steven,

Here's some test code for the controls which demonstrates the problems. First, the master control:

using System;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace OlbertMcHughLLC.Web.Controls
{
[ToolboxData("<{0}:OddPostBack runat=server></{0}:OddPostBack>")]
public class OddPostBack : CompositeControl
{
private int numChildren = -1;

[
Bindable(true),
Category("Data"),
]
public int NumChildren
{
get { return numChildren; }
set { numChildren = value; }
}

[
Bindable(true),
Category("Appearance"),
DefaultValue("Introduction"),
]
public string Introduction
{
get
{
if( ViewState["intro"] == null ) return String.Empty;

return (string) ViewState["intro"];
}

set { ViewState["intro"] = value; }
}

public override void DataBind()
{
Introduction = "This is a test";

if( numChildren <= 0 ) return;

for( int idx = 0; idx < numChildren; idx++ )
{
OddPostBackChild curSQ = this[idx];

if( curSQ is OddPostBackChild )
{
OddPostBackChild choiceSQ = curSQ as OddPostBackChild;
choiceSQ.DataSource = 5;
}

curSQ.Text = String.Format("Control {0}", idx + 1);
curSQ.QuestionID = idx;
}

DataBindChildren();
}

protected OddPostBackChild this[int idx]
{
get
{
EnsureChildControls();

return FindControl(String.Format("odd{0}", idx + 1)) as OddPostBackChild;
}
}

protected string ChildControlList
{
get
{
if( ViewState["questionSet"] == null ) return String.Empty;

return (string) ViewState["questionSet"];
}

set { ViewState["questionSet"] = value; }
}

protected override void CreateChildControls()
{
Controls.Clear();

if( numChildren > 0 )
{
// child control list stores, in the view state, the list of
// child controls to be recreated on postback. In this case it's a
// silly approach. In the real code, it's important because there are
// different child controls created for different entries in the data
// that we're binding to
StringBuilder temp = new StringBuilder();
for( int idx = 0; idx < numChildren; idx++ )
{
temp.Append('x');
}

ChildControlList = temp.ToString();
}

if( ChildControlList.Length == 0 ) return;

for( int idx = 0; idx < ChildControlList.Length; idx++ )
{
OddPostBackChild newCtl = new OddPostBackChild();
newCtl.ID = String.Format("odd{0}", idx + 1);

Controls.Add(newCtl);
}
}

protected override void RenderContents( HtmlTextWriter writer )
{
writer.Write(Introduction);
writer.WriteLine();

writer.WriteFullBeginTag("ol");
writer.WriteLine();

foreach( Control curQC in Controls )
{
if( curQC is OddPostBackChild )
{
writer.WriteFullBeginTag("li");

curQC.RenderControl(writer);

writer.WriteEndTag("li");
writer.WriteLine();
}
}

writer.WriteEndTag("ol");
writer.WriteLine();
}
}
}

Next, the detail control:

using System;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;

namespace OlbertMcHughLLC.Web.Controls
{
[DefaultProperty("Text")]
[ToolboxData("<{0}:OddPostBackChild runat=server></{0}:OddPostBackChild>")]
public class OddPostBackChild : CompositeControl
{
private Label lblQuestion;
private int numOptions = -1;

public OddPostBackChild()
{
}

[
Bindable(true),
Category("Data"),
DefaultValue(-1),
]
public int QuestionID
{
get
{
if( ViewState["questionID"] == null ) return -1;

return (int) ViewState["questionID"];
}

set { ViewState["questionID"] = value; }
}

[
Bindable(true),
Category("Appearance"),
DefaultValue("abcd"),
]
public string Text
{
get
{
EnsureChildControls();
return lblQuestion.Text;
}

set
{
EnsureChildControls();
lblQuestion.Text = value;
}
}

[
Bindable(true),
Category("Data"),
DefaultValue(-1),
]
public long QuestionNumber
{
get
{
if( ViewState["questionNum"] == null ) return -1;

return (long) ViewState["questionNum"];
}

set { ViewState["questionNum"] = value; }
}

[
Bindable(true),
Category("Data"),
]
public int DataSource
{
get { return numOptions; }

set
{
numOptions = value;

// this is a silly approach, but in the
// real code we store the number of entries in the list
// of complex objects that is being assigned to the datasource
NumChoices = numOptions;
}
}

[
Browsable(false),
]
public Label this[int idx]
{
get
{
EnsureChildControls();

return FindControl(String.Format("oddChildLabel{0}", idx + 1)) as Label;
}
}

protected int NumChoices
{
get
{
if( ViewState["numChoices"] == null ) return 0;

return (int) ViewState["numChoices"];
}

set { ViewState["numChoices"] = value; }
}

public override void DataBind()
{
if( numOptions <= 0 ) return;

for( int idx = 0; idx < numOptions; idx++ )
{
this[idx].Text = String.Format("&nbsp;Option {0}", idx + 1);
}
}

protected override void CreateChildControls()
{
Controls.Clear();

lblQuestion = new Label();
lblQuestion.ID = "lblQuestion";

Controls.Add(lblQuestion);

string quesID = "question" + QuestionID.ToString();

for( int idx = 0; idx < NumChoices; idx++ )
{
Controls.Add(new LiteralControl("<br />"));

HtmlInputRadioButton newCtl = new HtmlInputRadioButton();
newCtl.ID = String.Format("oddChild{0}", idx + 1);
newCtl.Name = quesID;
Controls.Add(newCtl);

Label newLbl = new Label();
newLbl.ID = String.Format("oddChildLabel{0}", idx + 1);
Controls.Add(newLbl);
}
}

public override void RenderBeginTag( HtmlTextWriter writer )
{
base.RenderBeginTag(writer);

writer.WriteFullBeginTag("p");
writer.WriteLine();
}

public override void RenderEndTag( HtmlTextWriter writer )
{
base.RenderEndTag(writer);

writer.WriteEndTag("p");
writer.WriteLine();
}
}
}

If you include an instance of OddPostBack on a plain aspx page with a submit button, and add the following code to the code-behind
file, you'll be able to demonstrate the problem:

protected void Page_Load( object sender, EventArgs e )
{
if( !Page.IsPostBack )
{
oddCtl.NumChildren = 3;
oddCtl.DataBind();
}
}

As a reminder, what you'll see is that the first choice made in any detail control is not saved on postback, while all subsequent
choices are saved on postback.

- Mark

Mar 1 '06 #3
Steven,

Some interesting follow-up testing results: the problem only manifests itself with HtmlInputRadioButton controls. If you replace the
line in the detail control which creates the radio button with, say, a checkbox:

for( int idx = 0; idx < NumChoices; idx++ )
{
Controls.Add(new LiteralControl("<br />"));

HtmlInputCheckBox newCtl = new HtmlInputCheckBox();
....

the problem goes away (i.e., even changes made on the first post are retained on postback). Same thing if you make it a textbox
instead.

So the problem seems to be something unique to HtmlInputRadioButton controls. I suspect it may have something to do with the fact
that they appear to have an additional "value" when they work as a group, namely, the name of the currently selected radio button.

Comments?

- Mark
Mar 1 '06 #4
Okay, problem solved, I think. It's the result of not initializing the detail control's properties in the proper sequence.
Specifically, in the DataBind() code for the master control:

public override void DataBind()
{
Introduction = "This is a test";

if( numChildren <= 0 ) return;

for( int idx = 0; idx < numChildren; idx++ )
{
OddPostBackChild curSQ = this[idx];

if( curSQ is OddPostBackChild )
{
OddPostBackChild choiceSQ = curSQ as OddPostBackChild;
choiceSQ.DataSource = 5;
}

// this next pair of lines causes the error. Assigning a value to the detail
// control's Text property causes an implicit EnsureChildControls() to be
// be called, which assigns the wrong "group name" to the radio buttons,
// because QuestionID hasn't been assigned yet.
// reversing the order of the assignments solves the problem.
curSQ.Text = String.Format("Control {0}", idx + 1);
curSQ.QuestionID = idx;
}

DataBindChildren();
}

I've seen numerous reports of these kinds of hair-pulling subtle bugs in ASPNET code. I think someone needs to sit down and rethink
the architecture, because sequence-specific dependencies like this are really, really annoying.

If you have any other suggestions on design patterns that can avoid these kinds of problems, I'm all ears.

- Mark
Mar 1 '06 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

10
by: dx | last post by:
I have the Microsoft Press: Developing Microsoft ASP.NET Server Controls and Components book. It's starting to shine some light on control development but there is something about composite...
2
by: John Lau | last post by:
Hi, Is there documentation that talks about the page lifecycle, the lifecycle of controls on the page, and the rendering of inline code, in a single document? Thanks, John
3
by: Martin | last post by:
Hi, I have created a composite control that has a number of standard asp.net controls on it that can themselves cause postbacks. What i need to do in my composite control is to determine which...
3
by: Beavis | last post by:
I hate to repost a message, but I am still at the same point where I was when I originally posted, and hopefully someone else will see this one... Ok, so I have gone off and documented the...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.