I have create a custom composite drop down list control that does not save view state for all properties. There is a requirement that this contol work in a Repeater. I have tested the contol in a repeater in various ways. This is my first post ever, so I apologive if I have provided too little, too much or the wron info.
Note: The Repeater was always created in the aspx file.
1) Page.EnableViewState = true , and my control added to the repeater programmatically via the ItemDataBound event.
2) Page.EnableViewState = false, and my control added to the repeater in the aspx file.
3) Page.EnableViewState = false, and my control added to the repeater programmatically via the ItemDataBound event.
4) Page.EnableViewState = true , and my control added to the repeater in the aspx file.
#4 is not working well. The only way I can get this to work is if I do not rebind the repeater on postback AND all of the properties of my custom control are persisted via viewstate or controlstate. This prevents the user from choosing to override some properties on postback and clogs up the viewstate. Is providing all of the properties of the child controls via viewstate (or control state) a requirement of a repeater or is something wrong with my code?
Note: All four cases work if the contol is placed in directly in the page. I only have problems in the Repeater.
If I try to rebind my Repeater under the condition of #4, then weird things happen:
1) LoadPostData fires 4 times for a two line repeater. All four firings occur during the 1st ProcessPostData event.
2) My RaisePostDataChangedEvent never fires.
3) CreateChildControls (and thus ItemDataBound) fires off in LoadState and agin during the page Load event and at this time it seems to have forgotten the selected key of my drop down list and other properites nor does this get corrected during the ProcessPostData Second try. In fact the control seems to have forgotten the data it got when it ran LoadPostData in step 1.
Note: My control does not get a list of data from the user for the drop down. It will be obtained elsewhere, by the control, in the final control, so it is hardcoded in the sample control provided.
Sample files are provided below ( your can assume that Page.EnableViewState = true)
<asp:Repeater ID="testRepeater" runat="server">
<HeaderTemplate>
<tr>
<th class="icon">
Header HNc1
</th>
<th class="icon">
Header 2
</th>
<th class="icon">
Header 3
</th>
</tr>
</HeaderTemplate>
<ItemTemplate>
<tr>
<td class="numeric">
<asp:TextBox ID="AllocationPercent" runat="server"></asp:TextBox>
</td>
<td class="icon">
<asp:TextBox ID="TextBox4" runat="server"></asp:TextBox>
</td>
<td>
<asp:Panel ID="panel1" runat="server">
<pv:MyControl ID="picker2" runat="server" />
</asp:Panel>
</td>
</tr>
</ItemTemplate>
</asp:Repeater>----------------------------------------------------------------------------
public partial class TestMyControlR : PlanViewPage
{
protected void Page_Load(object sender, EventArgs e)
{//*********************************************** Bind repeater ********************************************
testRepeater.ItemDataBound += new RepeaterItemEventHandler(testRepeater_ItemDataBound);
testRepeater.ItemCommand += new RepeaterCommandEventHandler(testRepeater_ItemCommand);Hashtable hash = new Hashtable();
hash.Add(1, "One");
hash.Add(2, "Two");testRepeater.DataSource = hash;
testRepeater.DataBind();
if (!Page.IsPostBack)
{Page.Trace.Write("Time to Bind the page");
Page.DataBind();
}
}public void TestControl_TextChanged(object sender, EventArgs e)
{MyControl myTestControl = (MyControl) sender;
}
void testRepeater_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
// Command handler goes here
}
}void testRepeater_ItemDataBound(object sender, RepeaterItemEventArgs e)
{if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
Page.Trace.Write("In testRepeater_ItemDataBound");MyControl picker2 =
(MyControl)e.Item.FindControl("picker2");if (!Page.IsPostBack)
{
picker2.SelectedKey = "THREE";
}
else
{
picker2.SelectedKey = "FOUR";
}
picker2.LabelText = "My Struct Picker";
picker2.SelectedIndexChanged += TestControl_TextChanged;
picker2.AutoPostBack = true;}
}
}
--------------------------------------------------------------------------------------------------------
public class MyControl : CompositeControl, IPostBackDataHandler
{
private Label _label;
private string _labelText = String.Empty;
private DropDownList _dropdownlist;
private string _selectedKey = String.Empty;
private StandardButton _btnViewAll;
private bool _autoPostBack = false;
private string _commandName = "CHANGE";
private string _commandArgument = String.Empty;public event EventHandler SelectedIndexChanged;
public string LabelText
{
get
{
object o = ViewState["LabelText"];
if (o == null)
{
return "Null String";
}
return (string) o;
}
set { ViewState["LabelText"] = value; }
}public string SelectedKey
{
get
{
return _selectedKey;
}
set
{
_selectedKey = value;
}
}public bool AutoPostBack
{
get { return _autoPostBack; }
set { _autoPostBack = value; }
}public string CommandName
{
get { return _commandName; }
set { _commandName = value; }
}public string CommandArgument
{
get { return _commandArgument; }
set { _commandArgument = value; }
}protected override void OnInit(EventArgs e)
{
base.OnInit(e);
Page.RegisterRequiresControlState(this);
Page.RegisterRequiresPostBack(this);
}protected override void CreateChildControls()
{
Page.Trace.Write("In CreateChildControlsForTesting for " + "ddList" + ID);if (_dropdownlist == null)
{
_dropdownlist = new DropDownList();
}if (_label == null)
{
_label = new Label();
}if (_btnViewAll == null)
{
_btnViewAll = new StandardButton();
}Controls.Add(_label);
Controls.Add(_dropdownlist);
Controls.Add(_btnViewAll);
_btnViewAll.Label = "View";
// DropDownList
_dropdownlist.SelectedIndexChanged += OnSelectedIndexChanged;}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);if (AutoPostBack)
{
_dropdownlist.AutoPostBack = true;
}_label.Text = LabelText;
// fill list
// _dropdownlist.ID = "ddList" + ID;
if (!Page.EnableViewState || !Page.IsPostBack || _dropdownlist.Items.Count == 0)
{
_dropdownlist.Items.Add(new ListItem("one", "ONE"));
_dropdownlist.Items.Add(new ListItem("two", "TWO"));
_dropdownlist.Items.Add(new ListItem("three", "THREE"));
_dropdownlist.Items.Add(new ListItem("four", "FOUR"));
}// can't set this until 1) have the list 2) have postback data
// You want the postback data to do the updating unless this is not a postback
if (SelectedKey != String.Empty)
{
_dropdownlist.SelectedValue = SelectedKey;
}
else
{
_dropdownlist.SelectedIndex = 0;
}
}protected virtual void OnSelectedIndexChanged(object sender, EventArgs e)
{
if (SelectedIndexChanged != null)
{
SelectedIndexChanged(this, e);
}
}protected static readonly object EventCommandObj = new object();
public event CommandEventHandler Command
{
add
{
Events.AddHandler(EventCommandObj, value);
}
remove
{
Events.RemoveHandler(EventCommandObj, value);
}
}//this will raise the bubble event
protected virtual void OnCommand(CommandEventArgs commandEventArgs)
{
CommandEventHandler eventHandler = (CommandEventHandler)Events[EventCommandObj];
if (eventHandler != null)
{
eventHandler(this, commandEventArgs);
}
base.RaiseBubbleEvent(this, commandEventArgs);
}protected virtual void OnCommandHandler(CommandEventArgs e)
{
OnCommand(e);
}#region IPostBackDataHandler Members
public bool LoadPostData(string postDataKey, NameValueCollection postData)
{
//TBD: Need to return true only if AutoPostBack is tuPage.Trace.Write("In LoadPostData");
// You do not always have an instantiated dropdownlist and you do not
// have control over its postback data. In order to ensure that the
// dropdownlist gets the latest data, update your SelectedKey property
// which you will then use to set the dropdownlist.
bool changed = false;
string postedValue = postData[_dropdownlist.UniqueID];if (postedValue != SelectedKey)
{
changed = true;
}SelectedKey = postedValue;
return changed;
}public void RaisePostDataChangedEvent()
{
if (
(!Page.EnableViewState || !EnableViewState) &&
String.IsNullOrEmpty(CommandName)
)
{
OnSelectedIndexChanged(this, new EventArgs());
}
else
{
OnCommandHandler(new CommandEventArgs(CommandName, CommandArgument));
}
}protected override object SaveControlState()
{
Page.Trace.Write("In SaveControlState for " + "ddList_" + ID);object baseState = base.SaveControlState();
Pair pair = new Pair(baseState, SelectedKey);
return pair;
}protected override void LoadControlState(object savedState)
{
Page.Trace.Write("In LoadControlState for " + "ddList_" + ID);Pair pair = savedState as Pair;
if (pair != null)
{
base.LoadControlState(savedState);
SelectedKey = (string)pair.Second;
}
}#endregion
}
}
![]() |
0 |
![]() |
Hi,
It's a shame this post never got an answer. I'm getting very similar behaviour. I have a Repeater whose ItemTemplate contains a single UserControl (which is contained in another .ascx file). This UserControl, and any controls inside it, are completely losing their ViewState on every postback. The Repeater was previously a ListView; I only changed it to a Repeater to test if that would make a difference.
The weird thing is, ViewState was working fine before. Since I've done a little restructuring of some parts of my application, suddenly the ViewState is disappearing. None of the restructuring should have significantly changed the lifecycle of the related controls, however something has changed.
The whole ViewState business seems very poorly worked out. I spend most of my development time battling against arcane nuances of this system. I've read up a number of detailed explanations of the page lifecycle and the mechanisms by which state is loaded, and every time I think I understand it all, something new goes wrong. I keep fighting to get my application working stably, only to find that as soon as I introduce some new feature, everything collapses again. It's getting really frustrating!
I'm now considering writing my own implementation of something similar to ViewState, so I can choose when to reload the state, rather than just vaguely pointing everything in the right direction and hoping (and/or praying) that ASP.NET gets it right.
In my particular scenario:
- The Repeater or ListView must have EnableViewState="false". The dataset it uses is large and I have no wish to bloat my ViewState when I can simply requery the required data on every postback.
- There is an additional point to the above, which is that if ViewState is enabled on the Repeater, then various things drastically stop working (like Button clicks). I think this might have something to do with issues described in this article: http://scottonwriting.net/sowblog/posts/1268.aspx
- The UserControl inside it therefore has EnableViewState="true", because that's where I need state data, each of these controls can be interacted with independently. As I say, this used to work fine. Suddenly, ViewState is being lost, even though Button clicks (and therefore all actual Post data within the control) are working.
Whatever I've done to break it, I now have to look back through my SVN logs and work out which particular innocent and insignificant-seeming code change actually resulted in this.
I don't really know where I can go with this, as it seems every time I fix one thing, something else breaks. I think constructing my own analog of ViewState is probably the best way, unfortunately that's yet another feature of the framework that I'll have had to drop, and I'm seeing less and less production benefits from using ASP.NET at all.
If anyone can point me in the direction of further information about exactly how I'm supposed to use ViewState, it'll provide fascinating reading, although I can never help thinking, "couldn't they have made this slightly simpler?"
/Serializer
![]() |
0 |
![]() |