Getting the old value of a asp.net control on postback - asp.net

I want to be able to find the old value of a asp.net control (textbox, checkbox etc) without relying on events (e.g. OnTextChange event). Something like "someTextboxControl.OldText" would be perfect! I imagine that it is stored in the viewstate and need to somehow get at it via a custom control. Any ideas?

The answer provided by Shrage was helpful, but it took some additional searching to make this solution work. I placed his code into the code behind within the namespace of the page I was working on. Additionally I had to register the control, and change update the control in question.
Register extended textboxex control; this goes right below the page directive
<%# Register TagPrefix="group" Namespace="ESM_Web_Portal" Assembly="ESM Web Portal" %>
Update the texbox you want to extend in the aspx page
<group:TextBoxEx ID="txbEditPartNumber" runat="server" Text='<%# Bind("part_number") %>'></group:TextBoxEx>
Then in code behind I was able to use the extended TextBox control
protected void CheckForExistingPartNumber(object sender, ServerValidateEventArgs e)
{
CustomValidator cv = ((CustomValidator)sender);
TextBoxEx tb = (TextBoxEx)cv.Parent.FindControl(cv.ControlToValidate);
if (SupplyBLL.GetSupplyByPartNumber(e.Value) != null && tb.Text != tb.OldText)
{
e.IsValid = false;
}
}

public class TextBoxEx:System.Web.UI.WebControls.TextBox
{
public string OldText { get; set; }
protected override bool LoadPostData(string postDataKey, System.Collections.Specialized.NameValueCollection postCollection)
{
OldText = Text;
return base.LoadPostData(postDataKey, postCollection);
}
}

Related

ASP.net Web Server Control, children are NOT maintenaing state

I am developing a custom server control, following is control's markup on my ASPX page,
<uc:NoteBook ID="nb1" runat="server">
<NotePages>
<uc:NotePage ID="p1" runat="server">
<asp:DropDownList ID="ddl1" runat="server"></asp:DropDownList>
</uc:NotePage>
</NotePages>
</uc:NoteBook>
Note that this control has NotePages property which is actually a collection of NotePage control. And NotePage control can have any type of children.
On the same aspx page, in Page_Load event handler, I am loading some items in DropDownList control,
if (!this.IsPostBack)
{
this.ddl1.Items.Add(new ListItem("Class-1", "C1"));
this.ddl1.Items.Add(new ListItem("Class-2", "C2"));
this.ddl1.Items.Add(new ListItem("Class-3", "C3"));
}
Problem:
Now problem is after Post Back, the DropDownList items get cleared. Which is clearly indicating I am missing some state management here.
Can anyone please guide my how to handle it?
Following is the control code.
public class NoteBook : CompositeControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public virtual List<NotePage> NotePages
{
get;
set;
}
public NoteBook()
{
this.NotePages = new List<NotePage>();
}
protected override void CreateChildControls()
{
foreach (var c in this.NotePages)
{
this.Controls.Add(c);
}
base.CreateChildControls();
}
}
[ParseChildren(false)]
public class NotePage : UserControl
{
}
Thanks.
You need to add the items when initializing the control not during the load event. Note: that the items need to always be added, not just when the page is not loading during a GET. By the time the load event has occurred the state has been initialized. See `Control Execution Lifecycle'
OK I solved it.
We just need to call the EnsureChildControls() method on control initialization, which causes child controls to be created before the ViewState/etc. things gets called.
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
this.EnsureChildControls();
}
I added above code in my NoteBook control.

Caching child user control only

There is parent user control, as seen below.
<%# Control Language="C#" AutoEventWireup="true" CodeFile="TestUserControl.ascx.cs" Inherits="TestUserControl" %>
<%# Register Src="~/UserControls/ChildUserControl.ascx" TagName="ChildUserControl" TagPrefix="FLI" %>
<div>
<FLI:ChildUserControl ID="child1" runat="server"/>
</div>
The child usecontrol has pulic property MatchDescription, which is set in the Page_Load of the parent control. I want to cache multiple versions of the child control, based on the MatchDescription property.
Problem is, the MatchDescription property cant be set in Page_Load, as the cached copy of the child control is used once its available.
How can i fix this problem?
Thanks!
It looks like using GetVaryByCustomString is the way to go here. My proof of concept consisted of the following:
WebUserControl.ascx: the test control. It has a single public property MatchDescription.
Global.asax: to override the GetVaryByCustomString method.
WebForm.aspx: a simple form to host the control.
WebUserControl.ascx
Add the following to the markup on the control:
<%# OutputCache Duration="120" VaryByParam="none" VaryByCustom="MatchDescription" %>
This specifies the duration (in seconds) to cache the control and VaryByCustom="MatchDescription" specifies the name of the parameter we will be caching on.
WebUserControl.ascx.cs
public partial class WebUserControl1 : System.Web.UI.UserControl
{
public string MatchDescription { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
object description = this.Context.Application["MatchDescription"];
if (description != null)
{
this.MatchDescription = description.ToString();
}
else
{
this.MatchDescription = "Not set";
}
Label1.Text = "Match description: " + this.MatchDescription;
}
}
This will check for the existance of the MatchDescription value. Because of the way the code in the parent page works, you should never see "Not set", though in your implementation it may be useful just in case the value is not set.
Global.asax
Add a Global.asax file to your project and add in the following method:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "MatchDescription")
{
object description = context.Application["MatchDescription"];
if (description != null)
{
return description.ToString();
}
}
return base.GetVaryByCustomString(context, custom);
}
This is the bit that checks for the MatchDescription associated with the cached control. If it is not found the control will be created as normal. context.Application is used because we need a way to communicate the description value between the parent page, the user control and the global.asax file.
WebForm.aspx.cs
public partial class WebForm : System.Web.UI.Page
{
private static string[] _descriptions = new string[]
{
"Description 1",
"Description 2",
"Description 3",
"Description 4"
};
protected override void OnPreInit(EventArgs e)
{
//Simulate service call.
string matchDescription = _descriptions[new Random().Next(0, 4)];
//Store description.
this.Context.Application["MatchDescription"] = matchDescription;
base.OnPreInit(e);
}
protected void Page_Load(object sender, EventArgs e)
{
var control = LoadControl("WebUserControl.ascx") as PartialCachingControl;
this.Form.Controls.Add(control);
//Indicate whether the control was cached.
if (control != null)
{
if (control.CachedControl == null)
{
Label1.Text = "Control was cached";
}
else
{
Label1.Text = "Control was not cached";
}
}
}
}
Note that in this code I am making/simulating the service call in the OnPreInit method. This is necessary as it occurs in the page lifecycle before the GetVaryByCustomString method.
Keep in mind that if a control has been cached, accessing it in the Page_Load method, for example, will require code of this form:
if (control is PartialCachingControl &&
((PartialCachingControl)control).CachedControl =!= null)
{
WebUserControl1 userControl = (WebUserControl1)((PartialCachingControl)control).CachedControl;
}
References:
My answer was inspired by: Any way to clear/flush/remove OutputCache?
I found the Pre_Init hint in this question:
Output Caching - GetVaryByCustomString based on value set in PageLoad()
This KB article discusses why the PartialCachingControl.CachedControl property can always return null:
http://support.microsoft.com/kb/837000

ViewState not persisting custom additions over postback

I have a question, and it may be because I'm not understanding the way viewstate works. I have some code that sets a viewstate variable in the setter for a property in a custom control I have made.
public bool EditMode
{
get { return (bool)(ViewState["editMode" + this.ID] ?? false); }
set {ViewState["editMode" + this.ID] = value;}
}
The editmode is being set during a button click event.
public void shippingButton_Click(object sender, EventArgs e)
{
if (((Button)sender).CommandName== "Edit")
{
ctrlShippingAddress.EditMode = true;
}
else
{
Page.Validate();
if (Page.IsValid)
{
ctrlShippingAddress.SaveAddress();
ctrlShippingAddress.EditMode = false;
}
}
}
I've tried manually setting it on page load in case I wasn't adding this to the viewstate at the correct point in the page cycle, but as I understand it events occur before render. I have also tried adding ViewStateMode="Enabled" to the control, then to the page using it, then to the master page with no luck.
If I debug at the point of the get/set I see that viewstate is an empty collection (which doesn't make sense because it's also saving form data that is persisting as it should).
I appreciate any help.
You don't need to append the control ID to the ViewState key. I assume you're doing that for uniqueness, but it's not necessary.
The above may fix your problem, but if not try something like this instead:
public bool EditMode
{
get
{
bool editMode = false;
if (ViewState["editMode"] != null)
editMode = (bool)ViewState["editMode"];
return editMode;
}
set
{
ViewState["editMode"] = value;
}
}
After reviewing with a co-worker I discovered that the issue was in the pages node of the web.config
<pages enableViewState="false">
needed to be set to true

Ad Hoc Styles in WebControl

I'm creating a WebControl that is used in several of my ASP.NET pages. In one instance, I'd like to add some ad hoc style attributes such as Width and Float.
Since I can't anticipate which attributes will be needed in the future, I'd like the markup using the control to be able to add any random style. I've got the control so it supports standard styles like Color, Width, etc., but not Float.
Is there any way to allow such attributes to be specified in the markup and have them propagate through to the rendered control unchanged? I'd like not to have to create my own custom Float property and any other possible style that might be needed.
I tried just adding style="..." in the markup, but this is simply stripped out and does not appear anywhere in the rendered control.
My previous answer pertains to User Controls, my mistake!
For a WebControl you can over ride the AddAttributesToRender method.
The following seems to work quite well:
[Bindable(true)]
[Category("Appearance")]
[DefaultValue("")]
[Localizable(true)]
public string style
{
get
{
String s = (String)ViewState["style"];
return ((s == null) ? String.Empty : s);
}
set
{
ViewState["style"] = value;
}
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
base.AddAttributesToRender(writer);
if(!string.IsNullOrEmpty(style))
{
writer.AddAttribute(HtmlTextWriterAttribute.Style, style);
}
}
EDIT: Changed public property to "style" to take advantage of intellisence.
I would add a CssClass property to your WebControl. This would allow any page that uses your control to supply its own look and feel.
It may not be what you are looking for but if you having a surrounding element you could apply the styles as a string as per the following:
.ascx
<%# Control Language="C#" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.cs" Inherits="HubbInvestor.WebUserControl1" %>
<div style="<%=AdHocStyle%>">
Some Text:
<asp:Button ID="Button1" runat="server" Text="A Button" />
</div>
.ascx.cs
public partial class WebUserControl1 : System.Web.UI.UserControl
{
private string adHocStyle = string.Empty;
public string AdHocStyle
{
get { return adHocStyle; }
set { adHocStyle = value; }
}
protected void Page_Load(object sender, EventArgs e)
{
}
}
of course you don't get any nice intellisense completion on the styles

Pass data to user components in asp.net

It is .net 2.0 here, not MVC, and I am crap at asp forms.
I have a page with user controls on it. When I click on something in the page, I want to load the usercontrol based on a parameter from the page.
I cannot do it.
In my page's FaultTree_Clicked, I get the value, then:
I tried exposing a property on the
child user control to set the value, which i set in FaultTree_Clicked,
it gets forgotten.
I tried saving it to
Session["mykey"], and loading
Session["mykey"] in the control's
Page_init... the value is blank.
I tried saving it to
ViewState["mykey"], and loading
ViewState["mykey"] in the control's
Page_init... the value is blank.
EDIT: more specific info:
Here is a cut down version of what the page(MyFault) looks like:
<form id="form" runat="server">
<div id="faulttree">
<asp:TreeView ID="FaultTree" ......>
</div>
<uc1:_DefectDetail ID="DefectDetail" runat="server" Visible="true" EnableViewState="true" />
</form>
And there is a method on the pages code behind "FaultTree_SelectedNodeChanged()".
When that method is hit, I want to load/show the DefectDetail control. The DefectControl requires a faultid, which comes off the Tree, which I successfully get in the SelectedNodeChanged method. I cannot get the faultid into the defect control.
This has to do with ASP.NET page lifecycle. By the time the click event fires, the control's init event has already happened.
In order to better assist you, please provide a more detailed explanation of what the FaultTree control is, what is the desired result and some sample code.
UPDATE:
Instead of a public property, you can simply create a public method in the control that does the desired action and invoke it from the FaultTree_SelectedNodeChangeEvent.
Example (for a public method named Refresh):
_DefectDetail.Refresh(object data);
Basically you have to use EventHandlers....
1. Add a event handler to your user control (I had a search bar UscSearchCriteriaBar1)
public event EventHandler CriteriaChanged;
+
private void InternalOnCriteriaChanged()
{
OnCriteriaChanged();
}
+
protected virtual void OnCriteriaChanged()
{
// If there are registered clients raise event
if (CriteriaChanged != null)
CriteriaChanged(this, EventArgs.Empty);
}
+
Example
public int EmployeeID
{
get
{
f (Session["EmployeeID"] != null)
{
ViewState["EmployeeID"] = Convert.ToInt32(Session["EmployeeID"]);
}
if (ViewState["EmployeeID"] == null)
ViewState["EmployeeID"] = 0;
return int.Parse(ViewState["EmployeeID"].ToString());
}
set
{
ctlEmployee.SelectedValue = value.ToString();
ViewState["EmployeeID"] = value;
Session["EmployeeID"] = value;
}
}
In your page or other control
override protected void OnInit(EventArgs e)
{
InitializeComponent();
UscSearchCriteriaBar1.CriteriaChanged += new EventHandler(this.CriteriaChanged);
base.OnInit(e);
}
private void CriteriaChanged(object sender, EventArgs e)
{
try
{
RefreshData();
}
catch (Exception ex)
{
ExceptionManager.Publish(ex);
}
}
You can get UscSearchCriteriaBar1.EmployeeID
This code should give you some ideas...was done for 1.1 should work on 2.

Resources