Dynamically created sub-classed dropdownlist not firing event - c#

There's loads of posts on this subject on the net, but I cant find one that fits my situation;
I've have a BasePage class, which my .aspx inherit from; I also have BaseLabel & BaseDDL classes, which extend Label & Dropdownlist respectively. On top of this I have a ReadyDDL class, which combines BaseLabel & BaseDDL into a single control (but this is a class, not a user control) and renders them with their own Div, Table, TableRow, TableCells, & another Label. The ReadyDDL class enables me to define label & dropdownlist & layout in a single html statement as per:
<moc:ReadyDDL ID="Person" runat="server" Member="#UserID" Caption="Create strike for"
DataSourceSQL="SELECT ID, UserName FROM [User] WHERE isDeleted = 0 AND ClientID = 3" TextField="UserName" ValueField="ID"
OnSelectedIndexChanged="ddl_SelectedIndexChanged" />
However I have a problem or two:
a) The event doesnt fire. The posts I have read on this subject say that the dropdown must be recreated OnInit & all will be fine. BUT -
I'm not dynamically creating a dropdownlist, but a custom extension of one - thus the code which creates the dropdownlist isnt in my aspx, where the event handler is defined, but is in a separate .cs file and accordingly, I cannot write
ddl.SelectedIndexChanged += new EventHandler(X);
because X doesnt exist in the class, only the page.
The only way I've found to get around this is to expose a string property (OnSelectedIndexChanged) which sets another property in BaseDDL, and when BaseDDL is rendered, to add the OnSelectedIndexChanged property to the markup produced.
The html produced looks ok, and on screen it looks ok, and it does postback when I change the selection in the control, but the eventhandler doesnt fire: it currently just contains a couple of assignment statements, which I have a breakpoint on, and which isnt reached.
On reflection, I suppose, rendering the handler only adds the event to the control in so far as the client is concerned, and the server doesnt know about it - but how can I overcome this, and define the handler at control initialisation, when the handler isnt in the same source code file as the initialisation code?
Does anyone have any ideas on either (1) getting the event to fire, or (2) how I can define the event in code, rather than via rendering?
Any questions please ask. Any help or suggestions will be appreciated, and I will mark Q as answered if suitable information comes.
b) the selected value is lost on postback. I know I have to do something with Viewstate, but I havent figured out just what, yet. If you know how I can implement a solution to this, a short example would be much appreciated.

Appears that your are developing a composite control - the correct way to go about this is to inherit from CompositeControl class and override CreateChildControls to add your child controls. This method is called by ASP.NET early in life-cycle and that would eliminate your view-state related issues.
See this article for developing composite control. For event, string typed property is not going to work - you must define the event at your composite control level. You can bubble up the child's event by raising your own event in the handler (this is shown in the article). Another way would be short-circuit the event handlers. For example, define the event in your composite control such as
public event EventHandler SelectedIndexChanged
{
add
{
childDdl.SelectedIndexChanged += value;
}
remove
{
childDdl.SelectedIndexChanged -= value;
}
}
childDll is reference to your child ddl control.

Related

Winform app, force execute OnLoad Event when focus is on another tab

I have a WinForm app, the form has TabControl, control has three tabs tabPage1,tabPage2,tabPage3.
The Tab 'tabPage3' is hosting a User defined control which internally has one or more child controls.
Now my problem lies in tabPage3,
I know it is a pure Winforms behavior, until your parent is not activated child controls Onload event won't fire.
I have a requirement to force the Onload event to fire when the focus is on tabPage1, tabPage2. Is there any way to force the Onload event to fire.
I have already visited following links but didn't find any clue. Link Link Link
This is a very unusual requirement, strongly smells like an XY problem. The Load event is heavily over-used in Winforms, a side-effect of it being the default event for a Form or UserControl. One of the behaviors inherited from VB6, the Load event was a big deal in that language. What you want can easily be accomplished by not giving Winforms a choice:
public UserControl3() {
InitializeComponent();
CreateHandle();
}
The CreateHandle() call does the forcing, OnLoad will immediately run. But do be aware that this happens very early, too early to do the kind of things that you'd really want to use OnLoad() or the Load event for. Which are rather limited, it is only truly necessary to discover the actual Location and Size of the control. Anything else belongs in the constructor. Surely including the code that you now run in OnLoad().
Strongly favor using the constructor instead.
I had a similar problem for a previous project, for my needs I managed to just iterate over every tab page in the forms constructor (or possibly OnLoad I can't remember) and then reset the index back to 0 before ever showing the end user.
Something similar to:
for(int i = 1; i < tabControl.TabCount; i++)
tabControl.SelectTab(i);
tabControl.SelectTab(0);

When to Call Method after Property Change

I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.

sharepoint webpart with many usercontrols

I have created sharepoint 2010 visual webpart in VisualStudio2010 with three user controls (.ascx). I want to dynamically change usercontrol in the webpart by clicking some button at currently loaded usercontrol. The main problem consist in the fact that buttonClick event is handled only after execution CreateChildControls method (where I try to get needed usercontrol using ViewData). Could anyone please help me to solve this problem?
Lee's response is basically right and may work well for you. However, you should not just use __doPostBack and rely that it will be always "magically" there for you. This method and variables mentioned by Lee are internal to ASP.NET and they are not meant to be used directly. Also, if you do not place any postback-ing control on your page this method will actually not be generated and your code calling it would fail.
Luckily, the code to cause and handle a generic postback is very simple. Instead of using built-in event handlers of input controls (which need to be constructed before being triggered - hence the call to CreateChildControls before your handler is called) you would target the postback to the Web Part itself:
public class MyWebPart : WebPart, IPostBackEventHandler {
protected override void CreateChildControls() {
Control clickable = ...; // Create a clickable control.
// Get JavaScript expression to send postback "test" to "this" web part.
var postBack = Page.ClientScript.GetPostBackEventReference(this, "test");
clickable.Attributes["onclick"] = postBack + "; return false";
Controls.Add(clickable);
}
void IPostBackEventHandler.RaisePostBackEvent(string eventArgument) {
if (eventArgument == "test") { // Recognize and handle our postback.
...
}
}
}
The GetPostBackEventReference will generate the necessary JavaScript expression for you. (And actually, just calling it makes the __doPostBack "magically" appear on the page.) The RaisePostBackEvent will be called between OnLoad and OnPreRender. Make sure not to cause child controls be created before that (by calling EnsureChildControls, for example, or by any other means). If you need multiple postback-ing controls the eventArguments parameter will let you differ among them.
You need the postback triggers in your user controls and not directly in the Web Part. I showed it in the Web Part just to keep it simple. You can put the result of GetPostBackEventReference to any control providing you use the right Page and Web Part instances when calling it.
--- Ferda
A way to do this would be have the button call a javascript function that in turn calls the following:
__doPostBack('LoadControl', 'ControlName');
You can then use the server variables __EVENTTARGET and __EVENTARGUMENT to find out which control to load within your CreateChildControls event handler.
I had that problem too.
Add this to the event handler (after executing your code inside the handler)
this.Page.Response.Redirect(HttpContext.Current.Request.Url.AbsoluteUri, true);
Regards,
Pedro

Passing value from One user control to another usercontrol

I have three usercontrols uc1.ascx ,uc2.ascx ,UC_Combined.ascx
UC1 has one label control
UC2 has one Dropdownlist Control
UC_Combined is created by combining UC1 and UC2
Now I placed UC_Combined.ascx on my aspx page webForm1.aspx has one more Label servercontrol
Now when I run my webForm1.aspx page I can see see DropDown list and a Label
Now when I select an Item from dropdown list ,I want the value of the selection to display to the Label
Can some one suggest me how can I do this .
It's not best to create a dependency between parent and child controls. Something you should generally avoid. But, if you have to do it or in some way makes your life alot easier then there are a few techniques for achieving this while keeping the controls somewhat decoupled. I would suggest you do the following:
Implement a PostBack handler that will store the value of the DropDownList in the "Items" collection of the HTTP Context (via HttpContext.Current.Items["ddlValue"] = val). The "Items" collection is a hashtable that has a lifespan of a single HTTP request. This means that it is cleared after the current HTTP request has been responded to. It's a nice lightweight means of transporting data between components.
Implement a property in UC1 that lazy loads the value from the "Items" collection and reference the property in your markup with the <%= %> syntax. Doing it this way ensures that you aren't trying to grab the value until Render (which is when the <%= %> code is executed), well after the PostBack handler event has been executed and the "Items" entry has been added. This way you can do everything within the same PostBack.
Think you got it?
Easy. Implement a event on the uc containing the drop down like:
public event EventHandler<DDSelectionChangedEventArgs> DDSelectionChanged;
public virtual void OnDDSelectionChanged(DDSelectionChangedEventArgs e)
{
if (DDSelectionChanged != null)
{
DDSelectionChanged(this, e);
}
}
The selection changed handler of the dd then have to call OnDDSelectionChanged.
Register a handler onto that event in your page (aspx). This handler should then call something like ChangeText(text) on the second uc with the textbox. And the textbox is been updated.
So the communication between the uc's is driven by events and the page has the resposibility to wire the events up. The uc's are completely independent.

How to pass method name to custom server control in asp.net?

I am working on a Customer Server Control that extends another control. There is no problem with attaching to other controls on the form.
in vb.net: Parent.FindControl(TargetControlName)
I would like to pass a method to the control in the ASPX markup.
for example: <c:MyCustomerControl runat=server InitializeStuffCallback="InitializeStuff">
So, I tried using reflection to access the given method name from the Parent.
Something like (in VB)
Dim pageType As Type = Page.GetType
Dim CallbackMethodInfo As MethodInfo = pageType.GetMethod( "MethodName" )
'Also tried
sender.Parent.GetType.GetMethod("MethodName")
sender.Parent.Parent.GetType.GetMethod("MethodName")
The method isn't found, because it just isn't apart of the Page. Where should I be looking? I'm fairly sure this is possible because I've seen other controls do similar.
I forgot to mention, my work-around is to give the control events and attaching to them in the Code-behind.
If you want to be able to pass a method in the ASPX markup, you need to use the Browsable attribute in your code on the event.
VB.NET
<Browsable(True)> Public Event InitializeStuffCallback
C#
[Browsable(true)]
public event EventHandler InitializeStuffCallback;
Reference:
Design-Time Attributes for Components and BrowsableAttribute Class
All the events, properties, or whatever need to be in the code-behind of the control with the browsable attribute to make it so you can change it in the tag code.
Normally you wouldn't need to get the method via reflection. Inside your user control, define a public event (sorry I do not know the vb syntax so this will be in c#)
public event EventHandler EventName;
Now, inside your aspx page, or whatever container of the user control, define a protected method that matches the EventHandler:
protected void MyCustomerControl_MethodName(object sender, EventArgs e) { }
Now, inside your markup, you can use
<c:MyCustomerControl id="MyCustomerControl" runat=server OnEventName="MyCustomerControl_MethodName">
Your workaround is actually the better answer. If you have code that you must run at a certain part of your control's lifecycle, you should expose events to let the container extend the lifecycle with custom functionality.
buyutec and Jesse Dearing both have an acceptable answer.
[Browsable(true)]
lets you see the property in the Properties window. However, the event doesn't show up, which makes no difference to me.
The thing I overlooked earlier was the fact that when you reference a control's even from the tag, it prep-ends On.
Every ASP.NET page is class of its own inherited from Page as in:
class MyPage : Page
Therefore, to find that method via Reflection, you must get the correct type, which is the type of the page class that stores the page code.
I suppose you need to support multiple pages for this control to be instantiated in I believe you can find the child type of any instance of Page via Reflection, but I do not remember how, but you should be able to do it.
but... like everyone else has said, such case is what events are for.

Categories

Resources