|
code
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
Control disappearstable. 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> 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 £½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½£½ I have now sent demo project to your email address to recreate the problem
described in this news posting. 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 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. 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 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? 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
Other interesting topics
CustomValidator
DropDownList Input Alternative to asp:MenuControl? open a new window with ajax reorder list Using the ImageUrl property of the HyperLink control Time tracking software with asp.net source code Enter key as select/submit button Unnecessary spaces when rendering list controls Link Button - Text on 2 lines RegularExpressionValidator problem |
|||||||||||||||||||||||