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.
Related
I've got a bit of an issue with creating a new control based on ASP.NET's ImageButton control. Everything works as expected, except for the click handler that is being hooked up in the control's OnInit override. Basically, clicking the custom image button just refreshes the page, never hitting the handler.
Now, I know this is something stupid I've done or just not understood, but I can't for the life of me figure this out. All the articles, questions and forum posts I've found on event handling issues for controls is for child controls, rather than ones that inherit from existing control types and have their own predefined handlers.
The following code is what I've written:
public class WebPaymentButton : ImageButton
{
public string DisabledImageUrl { get; set; }
public string TermsAcceptClass { get; set; }
protected override void OnPreRender(EventArgs e)
{
Page.ClientScript.RegisterClientScriptResource(typeof (WebPaymentButton), "PaymentModule.Scripts.WebPaymentButtonScript.js");
}
protected override void OnInit(EventArgs e)
{
CssClass = "WebPaymentButton";
if (!string.IsNullOrWhiteSpace(TermsAcceptClass))
{
Attributes["data-TermsClass"] = TermsAcceptClass;
}
if (!string.IsNullOrWhiteSpace(DisabledImageUrl))
{
Attributes["data-DisabledImageUrl"] = ResolveUrl(DisabledImageUrl);
}
Click += WebPaymentButton_Click;
base.OnInit(e);
}
private void WebPaymentButton_Click(object sender, ImageClickEventArgs e)
{
HttpContext.Current.Response.Redirect("http://dummy_payment_page_in_place_of_code", true);
}
}
I've tried hooking the handler up in the OnLoad and also switching it to run after the base.OnInit/OnLoad calls. Nothing has solved the handler issue. Can anyone point me in the right direction?
In case it helps, here is the markup for the button on the page:
<pm:WebPaymentButton runat="server" ImageUrl="~/pay-now.png" DisabledImageUrl="~/not-pay-now.png" TermsAcceptClass="TermsCheckbox" ID="MainPayButton" />
Have you tried overriding the OnClick event handler instead of hooking up to a new event handler?
Remove the Click += WebPaymentButton_Click line from OnInit and remove the WebPaymentButton_Click function, then add the following code to your class instead:
protected override void OnClick(ImageClickEventArgs e)
{
base.OnClick(e);
HttpContext.Current.Response.Redirect("http://dummy_payment_page_in_place_of_code", true);
}
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.
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...
}
}
I need the following functionality in my method: if the method is called before OnLoad event of ASP.NET life cycle throw an exception else continue execution of the method.
I was thinking of something like this:
if (Page.LifeCycleState < LifeCycleState.OnLoad) {
throw new InvalidPageStateException();
}
Is it possible to retrieve the state of ASP.NET page life cycle?
One approach would be to use a Basepage that you always use in your site. This would contain a variable called PageLoadComplete, which you would set at the end of your PageLoad event. Then you could check the state of this variable from within your method.
public abstract class BasePage : System.Web.UI.Page
{
public bool PageLoadComplete { get; private set; }
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
PageLoadComplete = true;
}
}
If you want to access the variable from code external to your page such as a UserControl, you would have to make it public and cast your page as BasePage.
public partial class MyUserControl : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
BasePage basePage = this.Page as BasePage;
if (basePage != null && !basePage.PageLoadComplete)
{
throw new InvalidPageStateException();
}
}
}
There is property in a realization of System.Web.UI.Control class(realization):
internal ControlState ControlState {
get { return _controlState; }
set { _controlState = value; }
}
Where ControlState is enum that contains members such as: Initialized, ViewStateLoaded, Loaded etc. here declaration
But as you can see this property is internal. So only way to get control state is proposed by Daniel Dyson.
You maybe able to find what you are looking for, by looking at the CurrentHandler and PreviousHandler properties of the current HttpContext.
if the method is called before OnLoad event of ASP.NET life cycle
throw an exception else continue execution of the method.
It is not clear which Onload event is meant, nor where the "method" resides. Is it the Page's Onload or a Control's OnLoad? Is it a Page's "method" or a Control's "method"?
Anyway, one can store sort of flag in the Context.Items Dictionary, which all controls (including Page) have access to during a request. This eliminates the need to use a general base page like suggested obove.
In the OnLoad method (no matter whether it is a Page's OnLoad or a Control's OnLoad):
Context.Items[UniqueID] = this;
In the "method":
if (Context.Items[UniqueID] != null)
{
throw new InvalidPageStateException();
}
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);
}
}