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
Related
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);
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.
Most often I use it when I am accessing a property of a composite control that depends on a child control. But I have also added it to OnInit of a control so I could make sure a hidden field was added correctly. Just a minute ago I called it in RenderControl because I was having an issue rendering a calendar extender and it fixed it. I am starting to get a little confused on when I need to and when I don't need to call EnsureChildControls and when I should call it. Any pointers are welcome. Thanks!
EnsureChildControls triggers CreateChildControl if it’s not already triggered before. This has to be done only one-time in the page life cycle. I call it unconditionally in OnInit / Page_Init and nowhere else. This place has the advantage that the controls are created before ASP.NET loads the ViewState. If you use the ViewState or ControlState it is necessary to create the child controls that early.
EnsureChildControls method makes sure child controls are created prior to accessing them.
Anytime you write composite controls for example, you want to build your controls inside the CreateChildControls events then call EnsureChildControls before accessing them to make sure all the controls have been created so you dont get a null reference exception.
I'm working on an ASP.NET project in which the vast majority of the forms are generated dynamically at run time (form definitions are stored in a DB for customizability). Therefore, I have to dynamically create and add my controls to the Page every time OnLoad fires, regardless of IsPostBack. This has been working just fine and .NET takes care of managing ViewState for these controls.
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
RenderDynamicControls()
}
private void RenderDynamicControls()
{
//1. call service layer to retrieve form definition
//2. create and add controls to page container
}
I have a new requirement in which if a user clicks on a given button (this button is created at design time) the page should be re-rendered in a slightly different way. So in addition to the code that executes in OnLoad (i.e. RenderDynamicControls()), I have this code:
protected void MyButton_Click(object sender, EventArgs e)
{
RenderDynamicControlsALittleDifferently()
}
private void RenderDynamicControlsALittleDifferently()
{
//1. clear all controls from the page container added in RenderDynamicControls()
//2. call service layer to retrieve form definition
//3. create and add controls to page container
}
My question is, is this really the only way to accomplish what I'm after? It seems beyond hacky to effectively render the form twice simply to respond to a button click. I gather from my research that this is simply how the page-lifecycle works in ASP.NET: Namely, that OnLoad must fire on every Postback before child events are invoked. Still, it's worthwhile to check with the SO community before having to drink the kool-aid.
On a related note, once I get this feature completed, I'm planning on throwing an UpdatePanel on the page to perform the page updates via Ajax. Any code/advice that make that transition easier would be much appreciated.
From Dirk to Dirk :-)
What do you mean with RenderDynamicControls? Create and set controls? If this is your intention not ASP.NET is managing your ViewState, but you do. If you fill the controls on every load, you always overwrite the existing ViewState!
If you want to use the ViewState, create your controls in the pages init event and fill them in the load event, but only if the request isn’t a postback. This is necessary, because ASP.NET recreates the ViewState between init and load. And this is also the reason for the two “rendering cycles” you describe. You need the first control creation cycle because ASP.NET can’t restore the ViewState without a proper control set and ASP.NET can’t react proper on your response without it.
Back to your code: In general your RenderDynamicControlsALittleDifferently wouldn’t work - because you create your controls too late in the pages life cycle and you would damage the ViewState by inserting new objects to the control collection. In a similar situation I solved this problem by a redirecting the page to itself (Response.Redirect). In this case RenderDynamicControls would do the job, based on a “little differently situation” after you change your internal state.
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.