Showing posts with label event. Show all posts
Showing posts with label event. Show all posts

Wednesday, March 28, 2012

LinkButtons programmically added to updatepanel click event not firing

I have a conditional updatepanel with a multiview control inside. The first view displays a list of items in a table with a linkbutton next to each one created programmically with an eventhandler for the click event. The click event changes the multiview's active view to the next view to display controls for editing that item. The item table's content is first initialized in the OnLoad() function inside a if(!isPostback). I plan to eventually have a save button in the second view that will update the table's content after that. (ScriptManager EnablePartialRender = "true").

Ex:

    for(int i = 0; i < itemcount; i++) { ... LinkButton itemedit = new LinkButton(); itemedit.Text = "Edit"; itemedit.Click += new EventHandler(itemedit_Click); table.Controls.Rows[i].Cells[1].Add(itemedit); ...}

When I view the page and click the linkbutton next to one of the items in the updatepanel it starts to update. However, the view never changes. Further tests have shown that the click event is never being handled. If I change the code so that the table is updated during initial load and postbacks, I can get the click event to fire once. After the multiview is goes back to the original view, the click event can no longer be raised until the whole page is reloaded.

So, what I am trying to figure out is how to create eventhandlers at runtime inside an updatepanel and getting them to fire on an update.

Well, I have found a solution. I desided to scale the problem down to the most basic functions by creating a test application containing an updatepanel, multiview control with two views, and some code in the background to create a linkbutton to switch from one view to the other. The page looks like this as generated by VS:

<%@. Page Language="C#" AutoEventWireup="true" CodeFile="dynamicevent.aspx.cs" Inherits="dynamicevent" %
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<atlas:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="True">
</atlas:ScriptManager>

</div>
<atlas:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:MultiView ID="MultiView1" runat="server" ActiveViewIndex="0">
<asp:View ID="View1" runat="server">
view1</asp:View>
<asp:View ID="View2" runat="server">
view2</asp:View>
</asp:MultiView>
</ContentTemplate>
</atlas:UpdatePanel>
</form>
</body>
</html>

The page is pretty basic and so is the code behind:

public partialclass dynamicevent : System.Web.UI.Page{protected void Page_Load(object sender, EventArgs e) { LinkButton button =new LinkButton(); button.Text ="switch"; button.Click +=new EventHandler(button_Click);this.View1.Controls.Add(button); }void button_Click(object sender, EventArgs e) {this.MultiView1.ActiveViewIndex = 1; }}

This code creates one LinkButton to switch from ActiveViewIndex=0 to 1 and it works just fine. However, on a larger scale, creating a hundred or more LinkButtons next to records that must be requested from a database each time the page handles a postback doesn't sound like a good idea to me. So, I moved by button creation code into an if(!IsPostBack) block:

protected void Page_Load(object sender, EventArgs e) {if (!this.IsPostBack) { LinkButton button =new LinkButton(); button.Text ="switch"; button.Click +=new EventHandler(button_Click);this.View1.Controls.Add(button); } }
The changes break the page. When I click the LinkButton, the updatepanel reloads the contents of the first view minus the linkbutton. The linkbutton is lost and its eventhandler with it. So, perhaps atlas requires that everything remain wired up perfectly up until after Page_Load() is called at least. I did some thinking and came up with some code that fixes the problem by using the cache to store the button to be rewired on the next postback. It appears that that is all the work I need to do to get my click handler to work. I think the benefits are significant enough although it is hard to see in this example: protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
LinkButton button =new LinkButton();
button.Text ="switch";
button.Click +=new EventHandler(button_Click);

this.View1.Controls.Add(button);
Cache.Insert("test", button);
}
else
{
((LinkButton)Cache["test"]).Click +=new EventHandler(button_Click);
this.FindControl(((LinkButton)Cache["test"]).Parent.UniqueID).Controls.Add(((LinkButton)Cache["test"]));
}
}

The code rewires the event handler during a postback. The Control.Parent .Controls.Add() does not work directly for the LinkButton in the cache but the FindControl() method works just fine. I believe that although they have the same name, the first parent of the LinkButton is not the same as the second one. I am new to ASP.NET v2 and Atlas but I do not remember coming across this problem in the past with .NET 1.1.

If anyone has a better solution, please let me know. At this moment I am facing writing some manager class to wire up all my eventhandlers on on each postback and I really would like a better way.


changed

this.FindControl(((LinkButton)Cache["test"]).Parent.UniqueID).Controls.Add(((LinkButton)Cache["test"]));
to
this.Controls.Add(((LinkButton)Cache["test"]));
and it works just fine. This means that it does not matter where the LinkButton is located, just that it is added back to the page.

Thanks a lot for the solution you posted. I have been facing the same problem and your solution worked for me. But, I wonder if there is some other solution to this issue. If I find any I will post it here.

Thanks again.


Yes, that's not specific to Atlas. Any control that's added dynamically to the control tree must be added back on every subsequent postback.

I came across another issue when I was working with this yesterday. What I saw was that after a few postbacks my dynamically added link buttons won't work in fact it threw an object required error. Reason, they were removed from the cache and hencethis.Controls.Add(((LinkButton)Cache["test"])); would not work.

So, I tried to check if the cache had the button in it and only then I would execute this statement else I would create new linkbuttons. But guess what, I got back to the same problem I started off with. The button would not fire the event.

In the solution we completely depend on the cache to make sure that the linkbuttons are wired to the respective events. What would happen when the buttons are removed from the cache for whatever reason, especially in a production environment?


Ok, I think I may have a solution for this...as bleroy indicated this is the default behavior with dynamically added controls (that you have to add them to the page on every postback). So, if you add the linkbuttons to the page on Page_Init instead of Page_Load, you will not have to worry about caching them in order to re-wire the controls with the events.

Let me know if this was helpful.


I'd like to respectfully point out that putting a control in the cache is a bad, bad, bad idea and you should never ever do it. One reason is that this maintains an in-memory reference to the instance of the control, hence to the corresponding instance of the page and thus to a huge object graph that should have been thrown away at the end of the request. There is no way this is not going to blow up whenever you get more than a few simultaneous requests.

You should only put data (that is, disconnected data) or output (i.e. strings) in cache...

List Box and the update panel

I would like to know the event name to use when an ASP List Box is used with the update panel control. I would like the updatePanel to fire when the ListBox Selected Index Changed event occurs.

Thank You for any help

George

<asp:ListBoxID="ListBox1"runat="server"DataSourceID="AccessDataSource1"DataTextField="product_name"DataValueField="product_id"Rows="30"CausesValidation="True"></asp:ListBox>

<asp:AccessDataSourceID="AccessDataSource1"runat="server"DataFile="~/App_Data/test.mdb"SelectCommand="SELECT [product_id], [product_name] FROM [Products]"></asp:AccessDataSource>

<asp:ButtonID="Button1"runat="server"Text="Button"/>

<atlas:UpdatePanelID="up222"Mode="Conditional"runat="server">

<Triggers>

<atlas:ControlEventTriggerControlID="ListBox1"EventName="SelectedIndexChanged"/> <!-- DOES NOT FIRE -->

<atlas:ControlEventTriggerControlID="Button1"EventName="Click"/>

</Triggers>

<ContentTemplate>

<asp:LabelID="Label2"runat="server"Text="Labe354"></asp:Label>

</ContentTemplate>

</atlas:UpdatePanel>

You have everything exactly right, though it might not actually be what you want. Every time the SelectedIndexChanged event is raised, or the button is clicked, the UpdatePanel will be updated. However, to get any of that to happen, a postback must occur. In your sample page, the only way to get that to happen is to click the button. To make the page update immediately when the list selection changes, add this property to your ListBox: AutoPostBack="true"

Thanks,

Eilon

ListBox - SelectedIndexChanged Problem

I have a data bound ListBox. When I click on an item it fires the SelectedIndexChanged event like it should, however, NONE of the items are marked as 'selected' in the event method. Any ideas?

Doh! Found the solution! I was rebinding the data to the listbox on postback and apparently this is what was causing the problems. I wrapped the data binding code in an IF statement that executes if IsPostBack is false.

This might be good to know for others out there.

Monday, March 26, 2012

ListBox not raising event or makes full postBack

I have a ListBox that needs to be updated depending on selections made on another ListBox. The problem I have is that theSelectedIndexChanged event is not raised. If I put AutoPostback="True" (which worked in my little test app that I created first), it performs a full PostBack.

Has anybody experienced something like this? Any ideas a more than welcome.

Hi,

Have you add the eventhandler to your listbox?
<asp:ListBoxID="ListBox1"runat="server"OnSelectedIndexChanged="ListBox1_SelectedIndexChanged">


Sure I did. And have something in the handler just to make sure the second ListBox is updated.


Does a single update panel contains both the listbox. If the parent listbox is outside the update panel then create a AsyncPostbackTrigger and assign that listbox as control.


Of course I added Triggers. These are basic things I need to do to get the AJAX part working.

Still can't figure out what is wrong.

Listening for propertyChanged events

I have seen several examples on how to raise an event when a property changes, but I haven't been able to find any examples on how to start listening for it. How would I listen for an event raised through this:

this.raisePropertyChanged('myProperty');

I guess I would have to use the $addHandlers method, but what is the name of this specific event?

you do:

someObj.add_propertyChanged(SomeHandlerMethod);

Cheers


Ehm... where does it say which property I'm listening for?


See if you have anything being passed as parameter in the method handler.

function SomeHandlerMethod(sender, args)
{

}


So what you are basically saying is that if I want to listen for a specific property to change, I will have to listen for all properties that change, and then check whch ones then actually did change?

Listening to a CascadingDropDown event. How?

Is there any way to call client-side javascript when the CascadingDropDown has successfully finished its population from a webservice? Please give a code sample.

The solution and code sample is availablehere (provided bychrispont. Thanks,chrispont!).

ListSearch Tab or enter key event?

Hello,

I'm pretty new to Ajax and Aps.net so hopefully this question can easily be answered.

I have a ListSearch that extends a dropdownlist. When a user types for the list search, the correct item is chosen but I also want the dropdownlist to fire an event, preferably SelectedIndexChanged, if the user clicks the key Tab or enter. Right now a tab shows the correct item when a Tab is clicked but no event is fired. The SelectedIndexChanged event fires when a user clicks changes a GridView. I want the grid view to also change when the tab or enter key is pressed when using the ListSearch Any Ideas?

thanks for the help.

We have anissue tracking the selectedindex change event. If the tab key is hit when the focus is in the listbox should cause the onblur event to fire and you can hook into that. See thisbug for the enter/tab autopostback issue. You could update it with your scenario as well to make sure that the fix is covers it.

I'm running into the same problem as you guys are. I love how ListSearch Ajax works, but can't use it because it doesn't fire an event for the dropdownlist or manage the tab index.

In the meantime, I found this add-on tool that does what I'm looking for. Of course, it comes with a price. :-(

Go check out the "Contact Information" section.

http://samples.asplib.net/AspLibSamples/ComboBox.aspx

ListSearch doesnt work with Dropdownlist AutoPostBack

ListSearch works fine finding a specific item on a dropdownlist, but doesn't fire an AutoPostBack event when I tab over (or tab out).

I found this 3rd party add-on control that does exactly what I'm looking for, but was wondering if there's a similar dropdownlist available from Microsoft.


Check out the "Contact Information" section.

http://samples.asplib.net/AspLibSamples/ComboBox.aspx


Also, any easy way to display the prompt text "left" of the dropdownlist. Apparently, there are only two options: top or bottom.

Thanks.


Hi,

You can use the following code to force a postback when the dropdownlist losts focus.


string script = ClientScript.GetPostBackEventReference(DropDownList1, "");
DropDownList1.Attributes.Add("onblur", script);

Hope this helps.


That did the trick! Thanks!!

ListSearchExtender it seems that onchange event is not fired on DropDown

I'm using ListSearchExtender and seems that onchange is not fired when you type in DropDown. Is it a known bug? Is there any work around?

This is a known issue and we are planning to fix it. You could work around it by changing the code to fire the event in _searchForTypedText after the selected index has been set. Thanks.
Thanks. I'll just wait for fix. Is new version of Ajax Toolkit coming in the beging of april?

Personally, I'd rather put it in _onKeyPress, like this:

if

(e.charCode == Sys.UI.Key.enter || e.charCode == Sys.UI.Key.tab) {

element.onchange(

this);

}

Or _onBlur


Sorry... better yet.

In _onBlur after removing the prompt div, you can use this:

if

(this._originalIndex !=this._newIndex) {

element.onchange(this);

}


My way to fire the onChange is to call the same javascripts function in bothonChange andonKeyUp:

<asp:ListBoxid="ListBox1"runat="server"onChange="DoSomething(this)"onKeyUp="DoSomething(this)"></asp:ListBox>

<cc1:listsearchextenderid="ListSearchExtender1"runat="server"TargetControlID="ListBox1"PromptCssClass="listSearch"></cc1:listsearchextender>


The list box extender now fires the change event and you should not need to workaround this anymore. Please download the latest version of the Toolkit which contains this fix.

Saturday, March 24, 2012

Little addition to $addHandler

Hi guys -

This comes in really handy sometimes. Since standard compliant browsers do not have window.event I emulate it via a 1 line addition (line 16 below) to $addHandler. It comes in really convenient and virtually no overhead.

12var $addHandler = Sys.UI.DomEvent.addHandler = function Sys$UI$DomEvent$addHandler(element, eventName, handler) {3 /// <param name="element" domElement="true"></param>4 /// <param name="eventName" type="String"></param>5 /// <param name="handler" type="Function"></param>6 var e = Function._validateParams(arguments, [7 {name: "element", domElement: true},8 {name: "eventName", type: String},9 {name: "handler", type: Function}10 ]);11 if (e) throw e;1213 if (element.addEventListener) {14 if (!handler._browserHandler) {15 handler._browserHandler = function handler$_browserHandler(e) {16 if (e) window.event = e;17 handler.call(element, new Sys.UI.DomEvent(e));18 }19 }20 element.addEventListener(eventName, handler._browserHandler, false);21 }22 else if (element.attachEvent) {23 if (!handler._browserHandler) {24 handler._browserHandler = function handler$_browserHandler() {25 handler.call(element, new Sys.UI.DomEvent(window.event));26 }27 }28 element.attachEvent('on' + eventName, handler._browserHandler);29 }30 if (!element._events) {31 element._events = {};32 }33 var eventCache = element._events[eventName];34 if (!eventCache) {35 element._events[eventName] = eventCache = [];36 }37 eventCache[eventCache.length] = handler;38}3940

Any chance of getting this included in AJAX? I hate hacking away at AJAX functions.

Hi,

just curious, in which scenarios do you think it could be useful?

Also, the code contains a bug: http://forums.asp.net/thread/1487616.aspx

Basically, when you attach the same handlers to multiple elements, the handler is executed under the scope of the first element that got it attached (due to the handler._browserHander flag).


Hey Garbin -

I am actually neglecting the standard in ignoring parameter (laziness and conciseness more than anything). Here's an example.

var a = 'something';
var handler = Function.createCallback(myFunction, a);
$addHandler(el, handler)

function myFunction(s) {
window.event.returnValue = false;
}

Basically I am too lazy to create a function that accepts both a string and the event so I just use the window.event with the addition of that line. It works best for me since I don't need to use it unless I want it.

Thanks for the bug report I will fix on my side tomorrow but this is one of the reasons I hate overriding AJAX functions since I have to stay up to date... I'd prefer this line to be included in the source. ;) Or maybe one day I'll sit down and fix up my code!


Hello again Garbin -

I came up with a way better scenario than my laziness!!!!

A good justification for including this is DragDropManager in the preview bits. If you look at the handlers they use window._event to pass around the current event.

Is this a better scenario than my laziness? ;)

Alex


The idea is not to reinvent the browser API. Assinging window.event is like adapting FF, for example, to work like IE.

You should just use the event object passed into the handler as a parameter. If you really need the raw event objects because it contains something browser specific, you can use the rawEvent field on it.


Oh and I wouldn't necessarly use the preview bits as a justification... they are preview bits afterall :)

Hi,

yes I remember the DragDropManager. If you browse the code of the slider behavior in the control toolkit, you'll notice that I had to assign the event object just received in a handler, to window._event in order to make it work... not a best practice I think :)


InfinitiesLoop - I understand your point and definitely think staying with the standards is a good thing. Here's my justification for breaking away from this. Since we cannot assign context via $addHandler(s) we would have to assign a value via the DOM element and assign an attribute as a way to pass a context and then retrieve it via event.target.attribute. DOM access is slow and memory bug prone (if what I am trying to pass is a function) - I would need to do clean up and all that comes with it.

What I can do is a create something along the lines of the below to pass along the event and a context.

var $createDomFn = function(instance, fn, context) {
return function(e) {
fn.apply(instance, [ context, e ]);
}
}

I am personally not a fan of this type of solution because I can't use standard Function.createCallback.

I find it way more convenient to be able to access window.event raw event and work with it. If I want to use the DomEvent version (which is not completely cross browser and I've reported this) I can use new Sys.DomEvent(window.event).

Just my 2 cents.


Hi,

could you elaborate on why you can't use Function.createCallback? I think a callback could be the best pattern that fits your scenario.


I would think that any context info your event handler needs that is specifically tied to the dom element, should be on the dom element or associated with a control that is attached to the dom element anyway. If the context isn't specifically tied to the dom element then creating a closure like you demonstrated makes sense, it's the same thing as creating an instance of a class, assigning some state to it, then using one of its methods as the event handler (using Function.createDelegate).

Maybe I'm not understanding exactly what you mean. What would you have done with attachEvent? addHandler has no take-away. Or do you mean you'd like the event info to be available from anywhere in the callstack (like what the drag drop manager is doing)? Isn't better design to have the methods accept parameters for exactly what they need? Its the old global variable issue... you want to limit state to where it is needed. If there's something generic you are calling into that may potentially need the event info but you don't know until runtime, then you can devise a mechanism to store the event object so it can be accessed as needed, but not on the global window object. Maybe a custom event args that is passed down, or some other static... and then, maybe not the entire event object, maybe a high level state object that has already undergone some interpretation.

It may be more convenient to have window.event, I'll give you that... but convenience isn't the only goal of a solid framework.


Garbin - you are right Function.createCallback would fit my scenario (I use my own flavor of this $createFn that combines createDelegate and createCallback), I did not realize it passes the arguments and then pushes the context to the end. Thanks.

InfinitiesLoop - I am not sure I agree with your argument that anything tied to a DOM element should be on the dom element. This can be slower than using a closure and there is clean up involved. I think you are right in saying this is a global variable issue except that I would take this further and say storing anything on a DOM element is essentially the same thing. I personally like to be able to access the window.event object from where I need it, rather than adding an extra variable to the scope of a handler. I also would think that passing around DomEvent in a closure to handlers is more memory and processor intensive than having a global object. I guess it would all depend on the scenario but I can see both ways window.event vs argument event being faster/better in different usages.

I think you may have convinced me to test out which is faster in a scenario where a lot of elements have the same hadler that is fired frequently.

Thanks! Glad to see someone cares about such little things.

Wednesday, March 21, 2012

LoadControlState event not firing in update panel.

I have a link which is wrapped in a update panel. This link works as an expand/collapse link. The expand/collapse status of the link is saved in the control state. But i found that the LoadControlState event does not fire but the SaveControlState does. Because of this, i always get the expand/collapse state of the link as Collapsed which is default. I am kind of stuck on this now. I dont want to use session as it does not sound like a session wide data. I tried using hidden variables but even that does not work with ajax because Request.Form always has old value.

The reason i want to use control state and not viewstate is becasue i want to disable the viewstate on this page.

Any help is much appreciated.

Nilesh

Hi Nilesh,

I tried using hidden variables but even that does not work with ajax because Request.Form always has old value.

What you mean?

If you want to store something,you can store them in javascript variables.

Please check this:http://forums.asp.net/p/1134520/1813803.aspx#1813803

it store the scrollLeft/scrollTop in javascript variables.

Best Regards.

loading an xml data island from within an UpdatePanel

Hi,

I am using an xml data island to hold some information client-side so that it's directly available inside client-side event handlers. I update the data island during a postback of an ASP.NET control. Everything works fine until I place the ASP.NET control inside an UpdatePanel so that it can update the control without refreshing the entire page. But now, even though the postback happens and the data island is updated on the server, the client-side copy of the data island doesn't get updated. What I need is to be able to update the control plus the data island, without refreshing the entire page.

I then tried placing the xml data island in a separate update panel that uses the ASP.NET control event as a trigger. That doesn't work because the <xml> tag isn't allowed inside an update panel.

Help! Any ideas?

Whoops. I had omitted the <ContentTemplate> tag inside the UpdatePanel. Now the tag is accepted and everything works. Yea!


MylesRip:

Whoops. I had omitted the <ContentTemplate> tag inside the UpdatePanel

Big Smile Problems that are easily fixed are great, aren't they?

-Damien