I have build a custom server control. Basically, this custom control is providing a textbox and a browser button that help users to fill the textbox. Behind the scene, I have a complex data structure that is describing the context and the user selection (simplified version below):
[Serializable]
public class PickerValue
{
public PickerType Type { get; set; }
public string TargetUrl { get; set; }
public static PickerValue Default()
{
return new PickerValue
{
TargetUrl = "/",
Type = PickerType.Value1
};
}
}
Then, in my control I declare a property of this type, maintaining in the view state:
public PickerValue Value
{
get
{
var obj = ViewState["Value"];
return (PickerValue)(obj != null ? obj : ViewState["Value"] = PickerValue.Default());
}
set
{
ViewState["Value"] = value;
}
}
Because my custom control hold such value and should raise postback events, I implemented IPostBackDataHandler
in my control to convert and read the JSON representation (using newtonsoft.json) of this value (this will be used by the javascript code):
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(EventArgs e)
{
if (ValueChanged != null) ValueChanged(this, e);
}
public bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
var presentValue = JsonConvert.SerializeObject(Value);
var postedValue = postCollection[postDataKey];
if (!string.IsNullOrEmpty(presentValue) && !presentValue.Equals(postedValue))
{
Value = JsonConvert.DeserializeObject<PickerValue>(postedValue);
return true;
}
else
{
return false;
}
}
public void RaisePostDataChangedEvent()
{
OnValueChanged(EventArgs.Empty);
}
And I also customize the rendering of the control to generate the proper <input type='hidden'>
related to my control's value:
protected override void RenderContents(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID + "_data");
writer.AddAttribute(HtmlTextWriterAttribute.Name, this.UniqueID);
writer.AddAttribute(HtmlTextWriterAttribute.Value, JsonConvert.SerializeObject(Value));
writer.RenderBeginTag(HtmlTextWriterTag.Input);
writer.RenderEndTag();
RenderChildren(writer);
}
Finally, I generate the textbox and the browse button using the (I guess) the proper way of using CreateChildControls
:
protected TextBox txtTargetUrl;
protected Button btnBrowse;
protected override void CreateChildControls()
{
this.txtTargetUrl = new TextBox();
this.Controls.Add(this.txtTargetUrl);
this.btnBrowse = new Button
{
Text = BrowseButtonText
};
this.Controls.Add(this.btnBrowse);
}
This is nearly working. My internal input value is populated properly by the code, the postbacks rebuild properly the internal value, but my textbox lose its value. After each postback, the textbox is remaining empty.
What have I to correct in my code to maintain child controls' state?