Showing posts with label custom. Show all posts
Showing posts with label custom. Show all posts

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

Wednesday, March 21, 2012

Web Service and Custom Type(Enumeration)

Hello,

I have a web service with a method called "Login" Login returns a custom enumeration type called "MethodStatus". I am trying to call this method in Atlas, my client code is below

 function OnbuttonGo_click() { //Call script proxy passing the input element data var object = Crisp.ParentWebService.MainService.Login( 'd','d', //params OnComplete, //Complete event OnTimeout //Timeout event ); return false; } function OnComplete(result) { alert(result); } function OnTimeout(result) { alert("Timed out"); }
This method works fine if I change the type in my Login status to String but when I use my enumeration it fails
giving me the following error message:
Key cannot be null.
Parameter name: key
Server Error in '/AtlasWebSite1' Application.
Key cannot be null.
Parameter name: key
Description: An unhandled exception occurred during the execution of the
current web request. Please review the stack trace for more information
about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Key cannot be null.
Parameter name: key
Source Error:
An unhandled exception was generated during the execution of the current
web request. Information regarding the origin and location of the
exception can be identified using the exception stack trace below.
Stack Trace:
[ArgumentNullException: Key cannot be null.
Parameter name: key]
System.Collections.Hashtable.ContainsKey(Object key) +2773989
System.Collections.Hashtable.Contains(Object key) +4
Microsoft.Web.Services.ClientProxyGenerator.EnsureNamespace(Type t) +46
Microsoft.Web.Services.ClientProxyGenerator.GenerateEnumTypeProxies(IEnumerable`1 enumTypes) +117
Microsoft.Web.Services.ClientProxyGenerator.GetClientProxyScript(WebServiceData webServiceData) +339
Microsoft.Web.Services.WebServiceClientProxyGenerator.GetClientProxyScript(HttpContext context) +263
Microsoft.Web.Services.RestClientProxyHandler.ProcessRequest(HttpContext context) +79
Microsoft.Web.Services.HandlerWrapper.ProcessRequest(HttpContext context) +30
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +401
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +117
Version Information: Microsoft .NET Framework Version:2.0.50727.42; ASP.NET Version:2.0.50727.42
     <!--
[ArgumentNullException]: Key cannot be null.
Parameter name: key
at System.Collections.Hashtable.ContainsKey(Object key)
at System.Collections.Hashtable.Contains(Object key)
at Microsoft.Web.Services.ClientProxyGenerator.EnsureNamespace(Type t)
at Microsoft.Web.Services.ClientProxyGenerator.GenerateEnumTypeProxies(IEnumerable`1 enumTypes)
at Microsoft.Web.Services.ClientProxyGenerator.GetClientProxyScript(WebServiceData webServiceData)
at Microsoft.Web.Services.WebServiceClientProxyGenerator.GetClientProxyScript(HttpContext context)
at Microsoft.Web.Services.RestClientProxyHa

You're doing something else wrong somewhere, and I don't know from your code what it is. I just did the part that you think is tripping you up, and it works fine. Code follows:

<... Html stuff that builds a pair of text boxes and a button ...>

<script type="text/javascript">

function button_Click()

{

var name = document.getElementById("userName");

var pass = document.getElementById("passWord");

requestService = TestEnums.MyService.Login(

name.value, pass.value

OnComplete,

OnTimeOut);

return false;

}

function OnComplete(result)

{

alert(result);

}

function OnTimeOut(result)

{

alert('timed out');

}

</script>

<... closing html stuff ...>

now the web service

<... web service header, using statements, and so on ...>

[WebMethod]

public Status Login(string a, string b)

{

if(a==b)

return Status.Authenticated;

else

return Status.Anonymous;

}

<.... Enum ...>

publuic enum Status

{

Anonymous,

Authenticated

}

Now, what happens is that the thing runs, and it alerts 0 or 1. I suspect you'd have to handle the enum in javascript as well in order to translate the enumeration into something meaningful on the client. But the bottom line is that passing a custom type is not error-generating action.


Hello,

I have fixed the problem now the problem was because I never set a namespace for the enumeration. I had set a namespace for my service and not for my enumeration.

Thank's alot for your help!

Rob


No problem, glad you found it!