Click event not firing on dynamically added LinkButton - c#

I have a LinkButton that is created dynamically in Load and then added to a control that resides on the Master page. Here is the original code used to create the LinkButton:
LockUnlock = new LinkButton() { ID = "LockUnlock", Visible = false };
LockUnlock.Click += LockUnlock_Click;
Now, when I first dug into this problem I thought it might be related to the ASP.NET life cycle so I moved the code to an override OnInit, but that didn't fix it.
I then moved on to setting the ID because that's not being done. So I added this line:
LockUnlock.ID = "LockUnlock";
and I tried that in both OnInit and Load - no luck.
Then I thought, because I'm adding this to a custom control that is actually part of a ContentPlaceHolder I may need to make the ID static to get this to work, so I added this:
LockUnlock.ClientIDMode = ClientIDMode.Static;
I have only tried that in the Load, but no luck, and honestly if it's not working in Load it's almost certainly not going to change anything in OnInit - that was just my first move and really a hail Mary in a lot of ways.
Alright, so now I'm at the point where I feel like it's related to the fact that the ResourceCenter is a custom control that is added to a ContentPlaceHolder and thus the event, though it's technically hooked up, can't be fired because of the context of the class. But I'm not sure where to go from here.
Do I need to add a shared class for the click? Do I need to hookup the click in the custom control and then delegate it from there somehow?
I would prefer not to use either of those solutions, but hey, we do what we have to do! I look forward to everybody's input on this.
EDIT
The code that adds the control to the ResourceCenter looks like this:
this.ResourceCenter.AddAdminLink(LockUnlock.Visible ? LockUnlock : null);
and the code inside the ResourceCenter control that adds to its list looks like this:
if (link == null) { return; }
var wrapper = new HtmlGenericControl("li");
wrapper.Controls.Add(link);
this.AdminLinkList.Controls.Add(wrapper);
where link is what was passed into the method by the aforementioned line.

It would have to be in the init. Also, try adding the control to the control's collection, before adding the event, as in:
LockUnlock = new LinkButton() { ID = "LockUnlock", Visible = false };
Panel.Controls.Add(LockUnlock);
LockUnlock.Click += LockUnlock_Click;

Related

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

ASP.NET - Accessing All ImageButtons on a Page and Putting Two Images on an ImageButton

I actually have two questions:
(1) Is it possible to put another image on top of an ImageButton that already has an ImageUrl set (without changing the ImageUrl - literally just add the second image 'on top')? Even by using CSS?
(2) I have a dynamically set number of ImageButtons contained within a ListView. When a user clicks on an ImageButton, I change the .CssClass property of the one clicked in order to 'highlight' it. My question is this: whenever an ImageButton is click, I need to not only highlight it, but make sure I unhighlight all the others. However, I'm having trouble getting the others. I get the clicked ImageButton using
((ImageButton)sender).CssClass = "SelectedImageButton";
in the event handler. However, how do I get all the others so I can set their style 'back' to the unhighlighted style?
Thanks in advance for any help!
UPDATE: ANSWERED!
I've solved the issue mentioned in (2) using the following algorithm. Note, I've marked #OFConsulting's answer below as the correct answer because without his algorithm, I would have never gotten the following algorithm (which came from tweaking his algorithm slightly). Thanks #OFConsulting!
// Cast the sender to an ImageButton to have the clicked ImageButton
ImageButton clickedImageButton = sender as ImageButton;
// The ListView has ListViewDataItems and the ImageButtons are in
// THOSE children controls, thus match on the ImageButtons' Parents' IDs
Control parentControl = clickedImageButton.Parent;
List<ListViewDataItem> allOtherImageButtons = MyListView.Controls.OfType<ListViewDataItem().AsQueryable().Where(i => i.ID != clickedImageButton.Parent.ID).ToList();
// Highlight
clickedImageButton.CssClass = "HighlightedStyle";
// Unhighlight
foreach (ListViewDataItem button in allOtherImageButtons)
{
// The ImageButton is always the 2nd child control of the ListViewDataItem
ImageButton childImageButton = (ImageButton)button.Controls[1];
childImageButton.CssClass = "NoHighlightedStyle";
}
For Part (1) of that question, setting the background image within your css class might do the trick, but you never really explained why you just couldn't change the ImageUrl. You can always throw everything on an update panel if you need it to be dynamic without the hassle of a bunch of script.
Part (2) seems pretty straight forward. Just use a little bit of linq against the relevant control collection within your page.
protected void ImageButton5_Click(object sender, ImageClickEventArgs e)
{
ImageButton clickImageButton = sender as ImageButton;
// This example assumes all the image buttons have the same parent.
// Tweak as needed depending on the layout of your page
Control parentControl = clickImageButton.Parent;
List<ImageButton> allOtherImageButtons = parentControl.Controls.OfType<ImageButton>().AsQueryable().Where(i => i.ID != clickImageButton.ID).ToList();
// Highlight
clickImageButton.CssClass = "WhateverHighlights";
// Unhighlight
foreach (ImageButton button in allOtherImageButtons)
{
button.CssClass = "WhateverClears";
}
}
Edit: One more thing. Make sure any controls you are adding dynamically get added before Page_Load (I.E. during Init). There are some viewstate issues associated with adding control too late.

Dynamically created sub-classed dropdownlist not firing event

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.

WPF - FindName Returns null when it should not

FindName is broken for me :(
The object I am looking for is there. I have proof.
Here is the scenario:
ToggleButton button = (ToggleButton)sender;
Popup popup = (Popup)button.FindName("popSelectIteration");
popup is null but not always. Just sometimes. But even when it is set to null the child I am looking for is there.
I put a break point in when it was null and grabbed these two screenshots.
The is where FindName is returning null for "popSelectIteration".
But if you dig into the watch, you see that the child is there.
So what am I missing? Why does FindName not find it? As you can see from the screen shot this is not a timing issue (the FindName watch is null but the direct path is fine).
Is there a better way to find a control?
Side Note: If you are intersted in the XAML for the toggle button in question it can be found in this question: WPF - FrameworkElement - Enumerate all decendents?.
Update: I did some digging to see why this fails some times and other times it works. I have an animation that calls NameScope.SetNameScope((DependencyObject)form, new NameScope()); (Full method code here). Right after that call the FindName starts to fail.
I don't really understand that call. I think I copied and pasted the code. Anyway, I commented it out. But I would love know why this is failing.
I would guess it has to do with the difference between the visual and logical tree. The control is in the logical tree but maybe the template for this control has not been applied yet and therefore FindName won't return anything useful.
You could try to call ApplyTemplate(); on the container first.
This would also explain why it returns something sometimes.
Try
LogicalTreeHelper.FindLogicalNode(button, "popSelectIteration");
Little late to the party (and not actually answer to OP question), but
when you add elements dynamically, they are not findable by FindName.
You need to register them by calling RegisterName.
Example:
string number = GenerateNumber();
Button myButton = new Button();
myButton.Content = number;
myButton.Name = "button_" + number;
RegisterName(myButton.Name, myButton);
Panel.Children.Add(myButton);
object o = Panel.FindName(myButton.Name);
Maybe someone might find this useful.
In my experience, this happens when you add items via code-behind. I've found that you can fool FindName() (or the animation framework) via name scopes. That is, when you create your control, you do
NameScope.GetNameScope(yourContainer).RegisterName("name of your control", yourControlInstance);
For this to work reliably, though, you must make sure that you unregister the name:
NameScope.GetNameScope(yourContainer).UnregisterName("name of your control");
Posting this for future reference.
I have meet the same question now, but I use the method like so:
#region Override - OnApplyTemplate
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.PART_ListViewLeft = GetTemplateChild(cPART_ListViewLeft) as ListView;
this.PART_ListViewCenter = GetTemplateChild(cPART_ListViewCenter) as ListView;
this.PART_ListViewRight = GetTemplateChild(cPART_ListViewRight) as ListView;
this.PART_GridViewLeft = GetTemplateChild(cPART_GridViewLeft) as DsxGridView;
this.PART_GridViewCenter = GetTemplateChild(cPART_GridViewCenter) as DsxGridView;
this.PART_GridViewRight = GetTemplateChild(cPART_GridViewRight) as DsxGridView;
if(this.PART_ListViewLeft!=null)
this.PART_ListViewLeft .AlternationCount = this.AlternatingRowBrushes.Count;
if(this.PART_ListViewCenter!=null)
this.PART_ListViewCenter .AlternationCount = this.AlternatingRowBrushes.Count;
if(this.PART_ListViewRight!=null)
this.PART_ListViewRight .AlternationCount = this.AlternatingRowBrushes.Count;
// ApplyTempleted = true;
CreateColumnLayout();
}
#endregion
If the Control is dynamic create and of which or whose container the 'Visibility' is set to hide or Collapsed, then the code this.PART_ListViewLeft = GetTemplateChild(cPART_ListViewLeft) as ListView; will always return null, the reason is that the datatemplete has not yet been applied before OnApplyTemplate being called.
I would suggest to avoid using FindName function, based on my experience, expecially problematic when you try to find something in the DataTemplate applied to some control.
Instead , if it possible (based on your software architecture) declare Popup in XAML and
refer to it like resource or use Binding to set some Model property to it's reference.
Good luck.
Try to use button.FindResource("popSelectIteration")
ellipseStoryboard.Children.Add(myRectAnimation);
containerCanvas.Children.Add(myPath);
After you add register the controls like
RegisterName("TextBlock1", Var_TextBox);
or
RegisterName(myRectAnimation.Name,myRectAnimation);
RegisterName(myPath.Name,myPath);

Problem with dynamic controls in .NET

Problem with dynamic controls
Hello all,
I'm wanting to create some dynamic controls, and have them persist their viewstate across page loads. Easy enough, right? All I have to do is re-create the controls upon each page load, using the same IDs. HOWEVER, here's the catch - in my PreRender event, I'm wanting to clear the controls collection, and then recreate the dynamic controls with new values. The reasons for this are complicated, and it would probably take me about a page or so to explain why I want to do it. So, in the interests of brevity, let's just assume that I absolutely must do this, and that there's no other way.
The problem comes in after I re-create the controls in my PreRender event. The re-created controls never bind to the viewstate, and their values do not persist across page loads. I don't understand why this happens. I'm already re-creating the controls in my OnLoad event. When I do this, the newly created controls bind to the ViewState just fine, provided that I use the same IDs every time. However, when I try to do the same thing in the PreRender event, it fails.
In any case, here is my example code :
namespace TestFramework.WebControls
{
public class ValueLinkButton : LinkButton
{
public string Value
{
get
{
return (string)ViewState[ID + "vlbValue"];
}
set
{
ViewState[ID + "vlbValue"] = value;
}
}
}
public class TestControl : WebControl
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
Controls.Clear();
ValueLinkButton tempLink = null;
tempLink = new ValueLinkButton();
tempLink.ID = "valueLinkButton";
tempLink.Click += new EventHandler(Value_Click);
if (!Page.IsPostBack)
{
tempLink.Value = "old value";
}
Controls.Add(tempLink);
}
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ValueLinkButton tempLink = ((ValueLinkButton)FindControl("valueLinkButton")); //[CASE 1]
//ValueLinkButton tempLink = new ValueLinkButton(); [CASE 2]
tempLink.ID = "valueLinkButton";
tempLink.Value = "new value";
tempLink.Text = "Click";
Controls.Clear();
Controls.Add(tempLink);
}
void Value_Click(object sender, EventArgs e)
{
Page.Response.Write("[" + ((ValueLinkButton)sender).Value + "]");
}
}
}
So, let's examine case 1, where the line next to [CASE 1] is not commented out, but the line next to [CASE 2] is commented out. Here, everything works just fine. When I put this control on a page and load the page, I see a link that says "Click". When I click the link, the page outputs the text "[new value]", and on the next line, we see the familiar "Click" link. Every subesquent time I click on the "Click" link, we see the same thing. So far, so good.
But now let's examine case 2, where the line next to [CASE 1] is commented out, but the line next to [CASE 2] is not commented out. Here we run into problems. When we load the page, we see the "Click" link. However, when I click on the link, the page outputs the text "[]" instead of "[new value]". The click event is firing normally. However, the "new value" text that I assigned to the Value attribute of the control does not get persisted. Once again, this is a bit of a mystery to me. How come, when I recreate the control in OnLoad, everything's fine and dandy, but when I recreate the control in PreRender, the value doesn't get persisted?
I feel like there simply has to be a way to do this. When I re-create the control in PreRender, is there some way to bind the newly created control to the ViewState?
I've struggled with this for days. Any help that you can give me will be appreciated.
Thanks.
ViewState-backed properties are only persisted to ViewState if the control is currently tracking ViewState. This is by design to keep ViewState as small as possible: it should only contain data that is truly dynamic. The upshot of this is that:
ViewState propeties set during the Init event are not backed to ViewState (because the Page has not yet started tracking ViewState). Thus Init is a good place to add controls and set (a) properties that won't change between postbacks (ID, CssClass...) as well as initial values for dynamic properties (which can then be modified by code in the rest of the page lifecycle - Load, event handlers, PreRender).
When dynamically adding controls in Load or PreRender, ViewState is being tracked. The developer can then control which propeties are persisted for dynamically added controls as follows:
Properties set before the control is added to the page's control tree are not persisted to ViewState. You typically set properties that are not dynamic (ID etc) before adding a control to the control tree.
Properties set after the control is added to the page's control tree are persisted to ViewState (ViewState tracking is enabled from before the Load Event to after the PreRender event).
In your case, your PreRender handler is setting properties before adding the control to the page's control tree. To get the result you want, set dynamic properties after adding the control to the control tree:
.
protected override void OnPreRender(EventArgs e)
{
base.OnPreRender(e);
ValueLinkButton tempLink = new ValueLinkButton(); // [CASE 2]
tempLink.ID = "valueLinkButton"; // Not persisted to ViewState
Controls.Clear();
Controls.Add(tempLink);
tempLink.Value = "new value"; // Persisted to ViewState
tempLink.Text = "Click"; // Persisted to ViewState
}
As others have statement you'll need to ensure that you are creating via the Init method. To learn more about the ASP.NET page life cycle check out this article: http://msdn.microsoft.com/en-us/library/ms178472.aspx
I'm already re-creating the controls in my OnLoad event.
That's your problem. OnLoad is too late. Use Init instead.
Thank you for your help, but I tried that and it didn't make a difference. Besides, OnLoad works just as well for dynamic controls as OnInit, as long as you give your controls the same IDs every time.
I believe that once you have added the dynamic controls to the page in PageLoad, the ViewState is bound to the controls and the "ViewState still needs to be bound" flag (in concept, not an actual flag) is cleared. Then, when you recreate the controls, the existing ViewState is no longer bound.
I faced something similar last year, only in my case I did not want the ViewState to rebind. My issue is that I was not recreating the previous controls, which is why I think that the pseudo-flag notion above applies.
Try calling Page.RegisterRequiresControlState(). You can also use RequiresControlState() to check if it's already been registered.
ViewState works on the Page and its child objects. The new control in [Case 2] has not been added to the Page (or any of its children). In fact, in case of the code above, the object will be out of scope as soon as the OnPreRender method ends and will be garbage collected.
If you absolutely have to swap out the control, you will need to remove the old control from its parent using Remove() method and add the new control at the right place using AddAt().
If the control was the only child of the parent, the code would be something like the following.
ValueLinkButton tempLink = new ValueLinkButton();
Control parent = FindControl("valueLinkButton").Parent;
parent.Remove(FindControl("valueLinkButton"));
parent.AddAt(0, tempLink);
Control added before SaveViewState method called in control life cycle should persist their values. I would concur with Joe's answer. Check this image
http://emanish.googlepages.com/Asp.Net2.0Lifecycle.PNG
I figured out yesterday that you can actually make your app work like normal by loading the control tree right after the loadviewstateevent is fired. if you override the loadviewstate event, call mybase.loadviewstate and then put your own code to regenerate the controls right after it, the values for those controls will be available on page load. In one of my apps I use a viewstate field to hold the ID or the array info that can be used to recreate those controls.
Protected Overrides Sub LoadViewState(ByVal savedState As Object)
MyBase.LoadViewState(savedState)
If IsPostBack Then
CreateMyControls()
End If
End Sub

Categories

Resources