Wednesday, March 28, 2012

Very confused about state in my behavior-derived class

I am writing a very simple custom control extender as a first step towards building something slightly more complex. The intention of the extender at this point is just to make a panel visible in the onmouseover event of the target control. The code below is the javascript file that is registered with the control. What I'm finding is that the property I create called _panelId is set properly when the class is initialized (I can tell by the alert box I put in set method), but the property shows up as undefined when the handler for the onmouseover event fires (once again I tested this with an alert box). It's as though the class doesn't know its own properties when it gets to the _onMouseOver function.

My appologies if I'm missing something stupid, but I've been looking at this code all afternoon and can't figure it out...

Type.registerNamespace('Ballito');Ballito.HoverPanelBehavior = function(element) { Ballito.HoverPanelBehavior.initializeBase(this, [element]);// Propertiesthis._panelId =null;// Eventsthis._onmouseoverHandler =null;this._onmouseoutHandler =null;}Ballito.HoverPanelBehavior.prototype = { initialize : function() { Ballito.HoverPanelBehavior.callBaseMethod(this,'initialize');this._onmouseoverHandler = Function.createDelegate(this,this._onMouseOver);this._onmouseoutHandler = Function.createDelegate(this,this._onMouseOut); $addHandler(this.get_element(),'mouseover',this._onMouseOver); $addHandler(this.get_element(),'mouseout',this._onMouseOut);this.get_element().className =this._nohighlightCssClass; }, dispose : function() {if (this._onmouseoverHandler) { $removeHandler(this.get_element(),'mouseover',this._onmouseoverHandler);this._onmouseoverHandler =null; }if (this._onmouseoutHandler) { $removeHandler(this.get_element(),'mouseout',this._onmouseoutHandler);this._onmouseoutHandler =null; } Ballito.HoverPanelBehavior.callBaseMethod(this,'dispose'); }, _onMouseOver : function(e) { alert('_panelId = ' +this._panelId); var panel = Sys.UI.DomElement.getElementById(this._panelId);if (panel) { panel.Visible ="true"; } }, _onMouseOut : function(e) { var panel = Sys.UI.DomElement.getElementById(this._panelId);if (panel) { panel.Visible ="false"; } }, get_panelId : function() {return this._panelId; }, set_panelId : function(value) {if (this._panelId !=value) {this._panelId =value;this.raisePropertyChanged('panelId'); } alert(this._panelId); }}Ballito.HoverPanelBehavior.descriptor = { properties: [ {name:'panelId', type: String} ]}Ballito.HoverPanelBehavior.registerClass('Ballito.HoverPanelBehavior', Sys.UI.Behavior);Sys.Application.notifyScriptLoaded();

Hi,

the problem is that you are defining delegates but you're not attaching them as event handlers:

$addHandler(this.get_element(),'mouseover',this._onMouseOver);$addHandler(this.get_element(),'mouseout',this._onMouseOut);
becomes:
$addHandler(this.get_element(),'mouseover',this._onmouseoverHandler);$addHandler(this.get_element(),'mouseout',this._onmouseoutHandler);

Hi Garbin,

Thanks very much! That did take care of my state problem. Unfortunately (for me at least), it only got me one step further. Now I get the ID of the panel just fine in the mouse over event, but the call to getElementById() returns null. The documentation on this method is somewhat limited, but it seems to say that if I use the syntax I have in my code (not specifying the parent of the element), document is assumed. My assumption has been that this refers to the document that is referencing the script, i.e. the current page. Is this a valid assumption and regardless of that is this a valid way to grab an element off the page that uses my extender control? If not, is there a way to do that?

Just to make sure I'm clear, I want to set the _panelId property to the ID of a panel control on the page housing the extender control and then to get a reference to that panel using the ID in my mouse over script so I can change properties on it. Is this possible, and if so am I trying to do it the best way?

Thanks for your help,

Lee


Hi,

are you sure that the client id of the panel matches the id that you're passing to getElementById()?

Regarding the extender, I suppose that you want to get a reference to the corresponding behavior, right? If so, you should set the BehaviorID property on the extender and then use its value to get a reference to the behavior, together with the $find method:

var behavior = $find('behaviorID');


Hi Garbin,

Thanks for your response. To answer your question, yes, I am very certain the ID of the panel matches the ID being passed to the getElementById() method. I check the value passed with the alert box immediately preceding the call to getElementById() and it definitely does match the ID of the desired panel on the page where the extender control is defined.

Regarding the second part of your post, I'm not sure exactly where your going. Do I need a reference to the behavior in the script? I was thinking that once I got the reference to the panel from getElementById(), I'd be able to directly set its properties and that that's pretty much all it would take at this point. Is this wrong? Sorry if I'm missing something obvious, just trying to get started here.

Thanks,

Lee


Hi,

sorry, I didn't want to confuse you. If you need a reference to the DOM element (the panel) then getElementById (or $get, a shortcut to access the same method) is the way to go. At this point, since you're getting an error, could you post a simple example that reproduces the problem?


 Hi Garbin,
 Here's the source for a simple page that duplicates the problem:
First, the C# class that derives from ExtenderControl and defines my little "HoverPanel" control:
using System;using System.Data;using System.Configuration;using System.Web;using System.Web.Security;using System.Web.UI;using System.Web.UI.WebControls;using System.Web.UI.WebControls.WebParts;using System.Web.UI.HtmlControls;using Microsoft.Web.UI;using System.Collections.Generic;namespace Ballito.CS{ [TargetControlType(typeof(Control))]public class HoverPanel : ExtenderControl {private string _panelId;public string PanelId {get {return _panelId; }set { _panelId =value; } }protected override void OnPreRender(EventArgs e) {base.OnPreRender(e);// Test for ScriptManager and register if it exists ScriptManager sm = Microsoft.Web.UI.ScriptManager.GetCurrent(Page);if (sm ==null)throw new HttpException("A ScriptManager control must exist on the current page."); sm.RegisterExtenderControl(this, FindControl(this.TargetControlID)); }protected override IEnumerable GetScriptReferences() { ScriptReference reference =new ScriptReference(); reference.Path = ResolveClientUrl("HoverPanel.js");return new ScriptReference[] { reference }; }protected override IEnumerable GetScriptDescriptors(Control targetControl) { ScriptBehaviorDescriptor descriptor =new ScriptBehaviorDescriptor("Ballito.HoverPanelBehavior", targetControl.ClientID); descriptor.AddProperty("panelId",this.PanelId);return new ScriptDescriptor[] { descriptor }; } }}

Next, the script file used by the control (largely the same one as in my original post):

// JScript FileType.registerNamespace('Ballito');Ballito.HoverPanelBehavior = function(element) { Ballito.HoverPanelBehavior.initializeBase(this, [element]);// Propertiesthis._panelId =null;// Eventsthis._onmouseoverHandler =null;this._onmouseoutHandler =null;}Ballito.HoverPanelBehavior.prototype = { initialize : function() { Ballito.HoverPanelBehavior.callBaseMethod(this,'initialize');this._onmouseoverHandler = Function.createDelegate(this,this._onMouseOver);this._onmouseoutHandler = Function.createDelegate(this,this._onMouseOut); $addHandler(this.get_element(),'mouseover',this._onmouseoverHandler); $addHandler(this.get_element(),'mouseout',this._onmouseoutHandler); }, dispose : function() {if (this._onmouseoverHandler) { $removeHandler(this.get_element(),'mouseover',this._onmouseoverHandler);this._onmouseoverHandler =null; }if (this._onmouseoutHandler) { $removeHandler(this.get_element(),'mouseout',this._onmouseoutHandler);this._onmouseoutHandler =null; } Ballito.HoverPanelBehavior.callBaseMethod(this,'dispose'); }, _onMouseOver : function(e) { alert(this._panelId); var panel = Sys.UI.DomElement.getElementById(this._panelId); alert(panel);if (panel) { panel.Visible ="true"; } }, _onMouseOut : function(e) { var panel = Sys.UI.DomElement.getElementById(this._panelId);if (panel) { panel.Visible ="false"; } }, get_panelId : function() {return this._panelId; }, set_panelId : function(value) {if (this._panelId !=value) {this._panelId =value;this.raisePropertyChanged('panelId'); } }}Ballito.HoverPanelBehavior.descriptor = { properties: [ {name:'panelId', type: String} ]}Ballito.HoverPanelBehavior.registerClass('Ballito.HoverPanelBehavior', Sys.UI.Behavior);Sys.Application.notifyScriptLoaded();

Finally, the markup for a simple page that uses the control:

<%@. Page Language="C#" %><%@. Register Namespace="Ballito.CS" TagPrefix="ballito" %><!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 id="Head1" runat="server"> <title>ASP.NET AJAX Behavior Sample</title></head><body> <form id="form1" runat="server"> <asp:ScriptManager ID="ScriptManager1" runat="server" /> <div> <asp:Panel ID="Panel1" runat="server" BackColor="Cyan" Height="50px" Width="125px" Visible="False"> </asp:Panel> <asp:Button runat="server" ID="SampleButton" Text="Submit Form" /> <ballito:HoverPanel ID="hoverPanel1" runat="server" TargetControlID="SampleButton" PanelId="Panel1" /> </div> </form></body></html>

The behavior I find is that when I run the page and mouse over the button, I can tell the onmouseover event in my control does fire since the alert box in it is displayed. This is the alert box right before the call to getElementById(). As I said in the last post, the alert box shows the value of the _panelId property properly ("Panel1" in this case). The problem is that the alert box that fires right after the getElementById() call shows that the value returned by getElementById() is null instead of [object].

Thanks for the help,

Lee


Hi,

you are setting the Visible attribute of the Panel to false. This means that the Panel won't be rendered on the page and this is the reason why you are getting a null reference.

If you want to hide the panel, you could style the Panel using display:none or visibility:hidden


Hi Garbin,

Well this finally got me going down the road. I'm finding there is still a lot for me to figure out, but I'm starting to get things to work in some sort of expected manner and that's usually what it takes for me to get movement when learning something new. Thanks for all your help.

Lee

No comments:

Post a Comment