Control disappears

J

Jakob Lithner

In my application I have a dynamic set of attributes stored in a separate
table. To handle this I created a usercontrol that takes a collection of
attributes. The idea is to show all relevant attibutes and make them editable
for the user. Each attribute has: Name, datatype and value.

I use the datatype to create a dynamic control (TextBox, CheckBox or
DropDownList) and put it in a table cell. It works fine.

The problem is when I want to save the values. I then iterate through my
repeater items and pick up all relevant information. The problem is that the
dynamic controls have disappeared! All controls are found except the ones
with the values.

I looked in the resulting webpage source and find all controls in the proper
place. The ASP generated ID and names look correct.

Any help very much appreciated!

ASPX code:
======================================
<%@ Control Language="C#" AutoEventWireup="true"
CodeBehind="ucAttributeList.ascx.cs"
Inherits="XXX.YYY.Web.AppControls.ucAttributeList" %>
<table>
<tr>
<th>
Name
</th>
<th>
Value
</th>
<th>
DataType
</th>
<th>
Description
</th>
</tr>
<asp:Repeater ID="repAttribute"
OnItemDataBound="repAttribute_ItemDataBound" runat="server">
<ItemTemplate>
<tr>
<td>
<asp:Literal ID="litAttributeDisplayName" runat="server"
/>
<asp:HiddenField ID="AttributeTypeID" runat="server" />
<asp:HiddenField ID="ObjectAttributeID" runat="server" />
</td>
<td id="valueCell" runat="server">
</td>
<td>
<asp:Literal ID="litDataType" runat="server" />
</td>
<td>
<asp:Literal ID="litDescription" runat="server" />
</td>
</tr>
</ItemTemplate>
</asp:Repeater>
<tr>
<td>
</td>
<td>
<asp:Button ID="btnSave" runat="server" Text="Save"
onclick="btnSave_Click" />
</td>
<td>
</td>
<td>
</td>
</tr>
</table>


C# code
======================================
public partial class ucAttributeList : UserControl
{
public new List<Attribute> Attributes
{
get
{
var attributes = new List<Attribute>();
foreach (var item in repAttribute.Items)
{
// Find controls
var repeaterItem = (RepeaterItem)item;
var AttributeTypeID =
(HiddenField)(repeaterItem.FindControl("AttributeTypeID"));
var ObjectAttributeID =
(HiddenField)(repeaterItem.FindControl("ObjectAttributeID"));
var valueCell =
(HtmlTableCell)(repeaterItem.FindControl("valueCell"));
var control = valueCell.FindControl("ctrlValue");

// Create attribute and interpret values
var attribute = new Attribute();
attribute.AttributeTypeID =
int.Parse(AttributeTypeID.Value);
attribute.ObjectAttributeID =
int.Parse(ObjectAttributeID.Value);
if (control is TextBox)
attribute.Value = ((TextBox)control).Text;
else if (control is CheckBox)
attribute.Value = ((CheckBox)control).Checked;
else if (control is DropDownList)
attribute.Value =
int.Parse(((DropDownList)control).SelectedValue);
else
throw new SewsException("Control {0} is not handled",
control.GetType());
attributes.Add(attribute);
}

return attributes;
}
set
{
repAttribute.DataSource = value;
repAttribute.DataBind();
}
}

protected void Page_Load(object sender, EventArgs e)
{
}

protected void repAttribute_ItemDataBound(object sender,
RepeaterItemEventArgs e)
{
switch (e.Item.ItemType)
{
case ListItemType.Item:
case ListItemType.AlternatingItem:

// Interpret data item
var attribute = (Attribute)e.Item.DataItem;

// Find controls
var litAttributeDisplayName =
(Literal)(e.Item.FindControl("litAttributeDisplayName"));
var AttributeTypeID =
(HiddenField)(e.Item.FindControl("AttributeTypeID"));
var ObjectAttributeID =
(HiddenField)(e.Item.FindControl("ObjectAttributeID"));
var valueCell =
(HtmlTableCell)(e.Item.FindControl("valueCell"));
var litDataType =
(Literal)(e.Item.FindControl("litDataType"));
var litDescription =
(Literal)(e.Item.FindControl("litDescription"));

// Display static data
litAttributeDisplayName.Text =
attribute.AttributeType.DisplayName;
AttributeTypeID.Value = attribute.AttributeTypeID.ToString();
ObjectAttributeID.Value =
attribute.ObjectAttributeID.ToString();
litDataType.Text =
attribute.AttributeType.AttributeDataType.Name;
litDescription.Text = attribute.AttributeType.Description;

// Handle dynamic control
AttributeDataTypeEnum dataType =
(AttributeDataTypeEnum)attribute.AttributeType.AttributeDataTypeID;
switch (dataType)
{
case AttributeDataTypeEnum.String:
case AttributeDataTypeEnum.int32:
case AttributeDataTypeEnum.float32:
var textBox = new TextBox();
textBox.ID = "ctrlValue";
textBox.Text = attribute.Value.ToString();
textBox.EnableViewState = true;
valueCell.Controls.Add(textBox);
break;

case AttributeDataTypeEnum.Boolean:
var checkBox = new CheckBox();
checkBox.ID = "ctrlValue";
checkBox.Checked = (bool)attribute.Value;
valueCell.Controls.Add(checkBox);
break;

case AttributeDataTypeEnum.Enum:
var dropDownList = new DropDownList();
dropDownList.ID = "ctrlValue";
dropDownList.DataSource =
AttributeEnumAdapter.GetAttributeEnums(attribute.AttributeTypeID);
dropDownList.DataValueField = "Value";
dropDownList.DataTextField = "Name";
dropDownList.DataBind();
dropDownList.SelectedValue = attribute.ValueText;
valueCell.Controls.Add(dropDownList);
break;

default:
throw new SewsException("AttributeDataType {0} is not
handled", dataType);
}
break;
}
}

/// <summary>
/// Save all values from GUI.
/// </summary>
protected void btnSave_Click(object sender, EventArgs e)
{
using (var dc = Sews2DataContext.CreateInstance())
{
foreach (var attribute in Attributes)
{
var dbAttribute = dc.Attributes.Single(a =>
a.AttributeTypeID == attribute.AttributeTypeID && a.ObjectAttributeID ==
attribute.ObjectAttributeID);
dbAttribute.ValueText = attribute.ValueText;
}
dc.SubmitChanges();
}
}
}


Generated web source:
==================================
<tr>
<td>
Reset to Default
<input type="hidden"
name="ctl00$MajorContent$ucAllocateDOToContainer$myAttributeList$repAttribute$ctl02$AttributeTypeID"
id="ctl00_MajorContent_ucAllocateDOToContainer_myAttributeList_repAttribute_ctl02_AttributeTypeID" value="19" />
<input type="hidden"
name="ctl00$MajorContent$ucAllocateDOToContainer$myAttributeList$repAttribute$ctl02$ObjectAttributeID"
id="ctl00_MajorContent_ucAllocateDOToContainer_myAttributeList_repAttribute_ctl02_ObjectAttributeID" value="0" />
</td>
<td
id="ctl00_MajorContent_ucAllocateDOToContainer_myAttributeList_repAttribute_ctl02_valueCell">
<input
id="ctl00_MajorContent_ucAllocateDOToContainer_myAttributeList_repAttribute_ctl02_ctrlValue"
type="checkbox"
name="ctl00$MajorContent$ucAllocateDOToContainer$myAttributeList$repAttribute$ctl02$ctrlValue" checked="checked" /></td>
<td>
Boolean
</td>
<td>
</td>
</tr>
 
V

Vince Xu [MSFT]

Hello Jakob,

Based on my understanding, you create the control dynamically, but it
disappeared after doing post back. Based on your code, it loads dynamic
controls not only on initial request but also on each post back.
Furthermore, the rendered HTML contains the dynamic control element. If I
have misunderstood you, please feel free to let me know.

If a control doesn't appear on the page, there are three potential causes I
think.
1. The related control HTML code is not rendered on the page.
2. Some CSS styles cause it hidden. For example, the control is set to
"display:none" or wrapped by some div elements with "display:none".
3. The generated HTML code is fine, but the browser has trouble to show it.

How did you view the HTML source code? By browser? It is not reliable
sometimes(especially, when we use Ajax in the web page).
I suggest you use "Web Developmenet Helper" (Page->DOM Inspector) to check
the present HTML code. Then is the HTML source code fine? Is the dynamic
control is rendered as expect?

If it is possible, could you please post more generated HTML? Or, it will
be better if I can browse and access your web page directly.




Sincerely,

Vince Xu

Microsoft Online Support

£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½

Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/en-us/subscriptions/aa948868.aspx#notifications.

MSDN Managed Newsgroup support offering is for non-urgent issues where an
initial response from the community or a Microsoft Support Engineer within
2 business day is acceptable. Please note that each follow up response may
take approximately 2 business days as the support professional working with
you may need further investigation to reach the most efficient resolution.
The offering is not appropriate for situations that require urgent,
real-time or phone-based interactions. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by contacting
Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/en-us/subscriptions/aa948874.aspx

£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½
 
J

Jakob Lithner

I have now sent demo project to your email address to recreate the problem
described in this news posting.
 
V

Vince Xu [MSFT]

Hello Jakob,

I checked your demo project, I found that the Repeater will be bound only
first page load. When you click the button, the dynamic controls will be
initialized and we can't find the "ctrlValue" control object by using
valueCell.FindControl("ctrlValue") after doing post back.
When we create the control dynamically, we have to create this dynamic
control in each page load. Otherwise, the dynamic control will not be
generated after doing post back.



protected void Page_Load(object sender, EventArgs e)
{
//if (!IsPostBack) // Please remove this code so that it can
bind repeater each time.
// {
var attributes = new List<Attribute>();

attributes.Add(new Attribute { Name = "Name", DataType =
"Text", Value = "Jakob" });
attributes.Add(new Attribute { Name = "Age", DataType =
"Text", Value = "46" });
attributes.Add(new Attribute { Name = "Favourite fruit",
DataType = "Enum", Value = "Apple" });
attributes.Add(new Attribute { Name = "Married", DataType =
"Boolean", Value = "true" });

myAttributes.Attributes = attributes;
// }
}

Sincerely,

Vince Xu

Microsoft Online Support
 
J

Jakob Lithner

The whole idea was to parse changed values and save to storage.
With your suggestion I just load the repeater with identical values each time.
 
V

Vince Xu [MSFT]

Hello Jacob,

As far as I know, if we bind the repeater each time, the control value
updated will still be saved.
For example, you can input a new value on the dynamic TextBox control in
repeater. After you click the button to save, besides building the TextBox
control dynamically again, the TextChanged event will be triggered and the
new value will be exported to the TextBox. So you can get the new data in
Button Click event.


Sincerely,

Vince Xu

Microsoft Online Support
 
J

Jakob Lithner

Hello Vince,

Sorry for the delay, I was sick a couple of days and off from work.

Thanks for your reply with adjustments!
It does work as you said.
But I can't understand why .....
How can the controls be loaded with updated values after a page load that in
fact resets everything?
It seems there is some kind of event order here that I have misunderstood.
Are the ViewState values actually applied AFTER the page load?
 
V

Vince Xu [MSFT]

Hello Jakob,

Hope you'll recover soon.

TextBox, DropDownList and CheckBox class inherit from IPostBackDataHandler.
To participate in postback data processing, a control must implement the
IPostBackDataHandler interface and render elements whose HTML name
attributes have unique values on the page. The IPostBackDataHandler
interface has the following two methods:
public interface IPostBackDataHandler {
bool LoadPostData(string postDataKey,
NameValueCollection postCollection);
void RaisePostDataChangedEvent();
}

Let's make an example for TextBox. Assuming you create TextBox dynamically,
in the Load Postback Data phase, which occurs before the Load phase, a page
looks at each name in the name/value form post collection and searches the
control tree for a control with a UniqueID that matches that name. If the
page finds such a control and that control implements IPostBackDataHandler,
the page invokes LoadPostData on that control. If your control does not
render the value of its UniqueID as the name attribute of a form element,
as an alternative, it can participate in the Load Postback Data phase by
invoking the RegisterRequiresPostBack method of the containing page in the
control's PreRender method. The concrete code of function "LoadPostData"
and property "Text" are as below:

public string Text
{
get
{
object text = ViewState["Text"];
if (text == null)
return string.Empty;
else
return (string)text;
}
set
{
ViewState["Text"] = value;
}
}


public bool LoadPostData(string postDataKey,
NameValueCollection postCollection)
{
string postedValue = postCollection[postDataKey];

if (!Text.Equals(postedValue))
{
Text = postedValue;
return true;
// It will call RaisePostDataChangedEvent() automatically.
}
else
return false;

}

Hence, in the Load Postback Data phase, it will check the old value and
replace the old value with the new value typed. The new value will be
stored in ViewState.
After that, it will call Page_Load so that the new TextBox instance will be
created on the page. However, ViewState is still there so that the TextBox
value will be reserved. When the new TextBox is re-generated with the same
ID in Page_Load, it will load the value from property "Text". Meanwhile,
the value in ViewState will be picked up. Hence, we can see that the new
value typed is still in the TextBox re-created.

The page life circle here is as below:
1. Firstly, the life of a page is began by a request sent.
2. The server determines the request is a postback or a new request and
sets the IsPostBack property.
3. Then, it goes into page initialization stage. In this stage, controls on
the page are available and each control's UniqueID property is set. Any
themes are also applied to the page. If the current request is a postback,
the postback data has not yet been loaded and control property values have
not been restored to the values from view state.
4. After that, if the current request is a postback, it will call
LoadPostBack to store the value into view state and control properties are
loaded with information recovered from view state and control state.
5. Then Page_Load will be called. In this stage, new control instance will
be created with the same UniqueID.
6. In page rendering phase, it will load value from property "Text" which
will access from ViewState. Since the UniqueID is not changed, the value is
still available to receive by this control.

(You can get more information about page life circle from
http://msdn.microsoft.com/en-us/library/ms178472.aspx)

But for some other controls which don't derive from
IPostBackDataHandler(ex, GridView), we have to re-bind the datasource of
them. Otherwise, the data will not be reserved.

Sincerely,

Vince Xu

Microsoft Online Support
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top