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
Related
I've 2 user controls named UCCreateProfile.ascx (used for creating/editing profile data) and UCProfileList.ascx (used to display profile data in GridView). Now whenever a new profile created I want to update my UCProfileList control to show new entry.
The best solution against above problem I've to go for Observer Pattern. In my case UCCreatedProfile is a Subject/Observable and UCProfileList is a Observer and as per pattern definition when observer initialized it knows who is my Subject/Observable and add itself into Subject/Observable list. So whenever a change occurred in Subject/Observable it will be notified.
This pattern best fit my requirements but I'm getting few problems to implement this describe as follows.
I'm working under CMS (Umbraco) and I don't have any physical container page (.aspx). What I've to do is find UCCreateProfile (Subject/Observable) in UCProfileList (Observer) onLoad event using following code.
private Control FindCreateProfileControl()
{
Control control = null;
Control frm = GetFormInstance();
control = GetControlRecursive(frm.Controls);
return control;
}
where GetFormInstance() method is
private Control GetFormInstance()
{
Control ctrl = this.Parent;
while (true)
{
ctrl = ctrl.Parent;
if (ctrl is HtmlForm)
{
break;
}
}
return ctrl;
}
and GetControlRecursive() method is
private Control GetControlRecursive(ControlCollection ctrls)
{
Control result = null;
foreach (Control item in ctrls)
{
if (result != null) break;
if (item is UCCreateProfile)
{
result = item;
return result;
}
if (item.Controls != null)
result = GetControlRecursive(item.Controls);
}
return result;
}
this way I can find the UCCreateProfile (Subject/Observable) user control in UCProfileList (Observer) but the way to find out the (Subject/Observable) is not so fast. As you can see I need to loop through all controls and first find the HtmlForm control and then loop through all child controls under HtmlForm control and find the appropriate control we're looking for.
Secondly, placement of the user controls in container if very important my code will only work if UCCreatedProfile.ascx (Subject/Observable) placed before UCProfileList.ascx (Observer) because this way UCCreateProfile will load first and find in UCProfileList. But if someone changed the position of these 2 controls my code will not work.
So to get rid of these problems I need some solution which works faster and independent of the position of the controls.
I've figured out some solution as described below. Please do let me know if it is a good way of doing this. If there is an alternative, please let me know.
I've a session level variable (a dictionary with Dictionary<ISubject, List<Observer>>) . No matter which user control initialized/loaded first, User Control will add itself into this dictionary.
If Subject/Observable added first, the corresponding observers will be found in this dictionary.
If Observer added first it will added to the dictionary with a null entry. When the Subject added, the association is made.
Regards,
/Rizwan
The Observer pattern is best implemented in .NET via events and delegates. If you use events and delegates, the Dictionary you mention becomes completely unnecessary. See for example this code below (only important pieces shown):
public partial class UserProfile : System.Web.UI.UserControl
{
//This is the event handler for when a user is updated on the UserProfile Control
public event EventHandler<UserUpdatedEventArgs> UserUpdated;
protected void btnUpdate_Click(object sender, EventArgs e)
{
//Do whatever you need above and then see who's subscribed to this event
var userUpdated = UserUpdated;
if (userUpdated != null)
{
//Initialize UserUpdatedEventArgs as you want. You can, for example,
//pass a "User" object if you have one
userUpdated(this,new UserUpdatedEventArgs({....}));
}
}
}
public class UserUpdatedEventArgs : EventArgs
{
public User UserUpdated {get;set;}
public UserUpdatedEventArgs (User u)
{
UserUpdated=u;
}
}
Now subscribing to the UserUpdated event from the UserProfile control on the UserListControl is as easy as this:
public partial class UserList : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
//Find the UserProfile control in the page. It seems that you already have a
//recursive function that finds it. I wouldn't do that but that's for another topic...
UserProfile up = this.Parent.FindControl("UserProfile1") as UserProfile;
if(up!=null)
//Register for the event
up.UserUpdated += new EventHandler<UserUpdatedEventArgs>(up_UserUpdated);
}
//This will be called automatically every time a user is updated on the UserProfile control
protected void up_UserUpdated(object sender, UserUpdatedEventArgs e)
{
User u = e.UserUpdated;
//Do something with u...
}
}
During the Page_Load, I would like to capture the control that performed the postback.
protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
}
// Capture the control ID here.
}
As usual, any ideas would be greatly appreciated!
For anyone who might interested in this (at least what worked for me). Cen provided the answer.
In your Page_Load event add:
Control c= GetPostBackControl(this.Page);
if(c != null)
{
if (c.Id == "btnSearch")
{
SetFocus(txtSearch);
}
}
Then in your basepage code add:
public static Control GetPostBackControl(Page page)
{
Control control = null;
string ctrlname = page.Request.Params.Get("__EVENTTARGET");
if (ctrlname != null && ctrlname != String.Empty)
{
control = page.FindControl(ctrlname);
}
else
{
foreach (string ctl in page.Request.Form)
{
Control c = page.FindControl(ctl);
if (c is System.Web.UI.WebControls.Button)
{
control = c;
break;
}
}
}
return control;
}
You can see the original post here
Hope this helps.
You can usually look at Request.Params["__EVENTTARGET"]. This variable will be populated as the result of the postback, and will hold the UniqueID of the control that caused it.
Unfortunately this doesn't work for button or imagebutton controls. For what looks to be a pretty robust method for getting it in these cases, you could check out this blog post.
If there's a way to do that, I'd love to know it too!
However, you can set up event handlers for each of the controls that can generate the postback, and handle events as they come in.
The problem with that is that these events are handled after Page_Load. So what you need to do in this approach is add a handler for Page_PreRender, and process the control input there. In the page life cycle, control events are after Load, but before PreRender.
You can use Request["__EVENTTARGET"] to get the ID of the control that invoked the postback.
I have an ASP.NET user control that is used in another use control. The parent user control uses data-binding to bind to a custom property of the child user control.
What method can I override or page event where I am ensured that the property state is set?
I think in a page it is PageLoaded versus the Page_Load override? I am looking for this in the user control because my property is always null even though it is set.
Thanks.
Example. This is in my user control. FilterEntryId is being bound from inside another user control:
protected int _filterEntry = -1;
public int FilterEntryId
{
get
{
return _filterEntry;
}
set
{
_filterEntry = value;
}
}
protected void Page_Load(dobject sender, EventArgs e)
{
FilterEntry always -1!!
}
The property is being set but never has value when Page_Load. The Page_LoadComplete may be the proper place but does not seem to be an option in user control. I've also tried Page_DataBind.
My hypothesis is that this is a page lifecycle issue but it may be something else.
Not sure what you need to do with that property but since you can't be sure when will be set. Can't you add your logic on the set of the property?
Another option would be a later event as PreRender.
public int FilterEntryId
{
get
{
return _filterEntry;
}
set
{
_filterEntry = value;
//HERE YOUR LOGIC
}
}
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.
I have your basic asp.net web form which contains some client-side JavaScript that forces the page to time out and then redirect after 5 minutes. Mainly to protect possibly sensitive information.
At timeout, I want to force a server post back allowing me to save the form values for future edits.
I have played with both ClientScript.GetPostBackClientHyperlink() and ClientScript.GetPostBackEventReference(). Both seem to cause EventValidation issues for me. Yes, I can turn off Event Validation but is there a different or better workaournd?
Ideally, I don’t want to invoke a control (which has to be displayed) but just cause a postback with some type of argument that I can recognize serverside as being the result of a timeout condition.
This may be overkill, but you could setup a javascript timer to fire a web service call. The .NET web service can accept the form data and save it.
No great solutions so I build my own. A pretty simple custom control. Comments welcome.
using System;
using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace BinaryOcean.Web.Library.WebControls
{
[ToolboxData("<{0}:PostBackTimer runat=\"server\" />"), DefaultProperty("Seconds"), DefaultEvent("Timeout")]
public class PostBackTimer : Control, IPostBackEventHandler
{
public PostBackTimer() { }
public string CommandArgument
{
get { return (string)ViewState["CommandArgument"] ?? string.Empty; }
set { ViewState["CommandArgument"] = value; }
}
public string CommandName
{
get { return (string)ViewState["CommandName"] ?? string.Empty; }
set { ViewState["CommandName"] = value; }
}
public bool Enabled
{
get { return (bool)(ViewState["Enabled"] ?? true); }
set { ViewState["Enabled"] = value; }
}
public int Seconds
{
get { return (int)(ViewState["Seconds"] ?? 0); }
set { ViewState["Seconds"] = value; }
}
[Description("PostBackTimer_OnTimeout")]
public event EventHandler Timeout = delegate { };
[Description("PostBackTimer_OnCommand")]
public event CommandEventHandler Command = delegate { };
public void RaisePostBackEvent(string eventArgument)
{
Timeout(this, EventArgs.Empty);
Command(this, new CommandEventArgs(CommandName, CommandArgument));
}
protected override void OnPreRender(EventArgs e)
{
if (Enabled && Seconds > 0)
{
var postback = Page.ClientScript.GetPostBackEventReference(this, null);
var script = string.Format("setTimeout(\"{0}\",{1});", postback, Seconds * 1000);
Page.ClientScript.RegisterStartupScript(GetType(), "PostBackTimer_" + UniqueID, script, true);
}
base.OnPreRender(e);
}
}
}
Couldn't you use a javascript timer to "click" the submit button? It sounds like using this form would be really annoying though, if it keeps posting back while you're trying to fill it out.
with javascript, call __doPostBack("clientIdOfSubmitButton", null). This will fire off a postback just as if that button (or any other control you want) had triggered it.