Just a little snippet of jQuery that i find useful for toggling selection of items in an asp.net CheckBoxList control.
function toggleChecks(b) {
$(".recipientList input[type=checkbox]").each(function() {
if (typeof this.checked != "undefined") {
if (b == null)
this.checked = (!this.checked);
else
this.checked = b;
}
});
}
Now you can have 3 links/buttons/images alongside your CheckBoxList ? One to toggle the checked state of all checkboxes, one to set all checkboxes to checked, and finally one to uncheck all checkboxes.
Setting the parameter ?b? to null means ?toggle the state?, true = checked and false = unchecked.
My HTML for the links looks like this?
<a id="selectAll" class="selectionToggle" onclick="toggleChecks(true);return false;" href="#">Select All</a>
<a id="selectNone" class="selectionToggle" onclick="toggleChecks(false);return false;" href="#">Select None</a>
<a id="toggle" class="selectionToggle" onclick="toggleChecks(null);return false;" href="#">Invert Selection</a>
You can very easily wire the Javascript function into a jQuery plugin for all your projects.
For the purposes of my next post, I built a neat little edit in place behavior and I thought it deserved its own post. It does a pretty good job at showing how easy it is to build a clean behavior using ASP.NET Ajax. It?s always good to go back to the basics? In this post, I?ll show you how the behavior works, but more importantly how I built it.
The behavior is pretty similar to the in-place editing behavior Nikhil built a while ago (but it works against the latest Ajax framework). It attaches to any text input or text area. In the absence of JavaScript, nothing happens and the input is directly usable, which makes for a graceful degradation story. When JavaScript is enabled, the behavior hides the input and replaces it with a span that has the same dimensions and the same contents. Clicking on the span hides it, brings back the input and gives it focus, enabling edition of the value. When focus moves away from the input, the behavior hides the input again, copies the new value into the span and brings it back. Please note that for accessibility, you can also tab to the span instead of clicking on it. Check out the video, it shows that much better than any text description ever could:
The EditInPlace behavior does all that in 80 lines of readable code, doc-comments included. Before we look at some key parts of the code, let?s look at how you can use the control:
<input type="text" id="email" value="bleroy@example.com"/> <script type="text/javascript" src="Script/MicrosoftAjax.js"></script> <script type="text/javascript" src="Script/EditInPlace.js"></script> <script type="text/javascript"> Sys.Application.add_init(function() { $create(Bleroy.Sample.EditInPlace, { cssClass: "editInPlace" }, {}, {}, $get("email")); }); </script>
Include the library and behavior scripts, throw in some input or text area, $create the behavior over the input, setting properties such as cssClass if you have to. In the example, I?ve defined the style ?editInPlace:hover? in the CSS so that the user gets this nice hint something might happen if you click when you hover over the control. This is the effect you see in the first few seconds of the video. Pretty simple.
Of course, you can also use the new declarative syntax once you?ve included MicrosoftAjaxTemplates.js:
<body xmlns:sys="javascript:Sys" sys:activate="*" xmlns:inplace="javascript:Bleroy.Sample.EditInPlace"> <input type="text" id="email" value="bleroy@example.com" sys:attach="inplace" inplace:cssclass="editInPlace" />
Here, the xmlns are the equivalent of server-side @Register directives in ASP.NET, sys:activate asks the framework to instantiate all declarative controls on the page and sys:attach does the actual instantiation. The properties of the behavior are set by using attributes with the prefix that we defined earlier for it. This declarative snippet is equivalent to the previous one that was using $create.
Let?s now look at a few aspects of building this behavior. First, let?s look at the skeleton of the code:
/// <reference name="MicrosoftAjax.js"/> Type.registerNamespace("Bleroy.Sample"); Bleroy.Sample.EditInPlace = function(element) { Bleroy.Sample.EditInPlace.initializeBase(this, [element]); } Bleroy.Sample.EditInPlace.prototype = { } Bleroy.Sample.EditInPlace.registerClass(
"Bleroy.Sample.EditInPlace", Sys.UI.Behavior);
Here, we?re declaring the only dependency of this file using an XML doc-comment. This will point Visual Studio to the Ajax library so that we get IntelliSense when for example we type ?Sys.? from within that file.
Then, we?re declaring the Bleroy.Sample namespace, which is where we?ll build the behavior.
The behavior itself is defined on the next line. A type in JavaScript and Microsoft Ajax really is the same objet as its constructor. Something to get used to? The only thing the constructor does for the moment is calling its base constructor using initializeBase. In its complete version, it also does some checking on the target element?s tag name to check it?s either a text area or an input, and it initializes private fields, which is good practice for a better and more predictable debugging experience.
The next block is where the meat of the component is going to go: the prototype. This is where we?re going to define properties, events and methods.
Last, we register the class with the framework using registerClass, pointing it to its base class, Sys.UI.Behavior. A behavior is a component that attaches to an HTML element, like a control, except that there can be only one control per element (DataView is a control for example) whereas there can be as many behaviors as you want per element (which enables for example a watermark, an auto-complete and an edit in place behavior to be combined on the same textbox).
Let?s now look at the initialization phase of the behavior, where the events will get hooked from and the span will be created and added to the DOM:
initialize: function() { Bleroy.Sample.EditInPlace.callBaseMethod(this, "initialize"); var elt = this.get_element(), span = this._span = document.createElement("SPAN"); var bounds = Sys.UI.DomElement.getBounds(elt); span.style.position = "absolute"; span.style.width = bounds.width + "px"; span.style.height = bounds.height + "px"; span.tabIndex = elt.tabIndex; span.className = this._class; Sys.UI.DomEvent.addHandlers(this._span, { click: this.beginEdit, focus: this.beginEdit }, this); elt.parentNode.insertBefore(this._span, elt); this._oldDisplay = elt.style.display; this._inputBlur = Function.createDelegate(this, this.endEdit); Sys.UI.DomEvent.addHandler(elt, "blur", this._inputBlur); this.endEdit(); }
The method first calls base using callBaseMethod. Then it creates the span element and sets its position and size to be exactly equivalent to the input tag it will have to replace so that it doesn?t upset the layout of the page. It also sets the CSS class to the current value of the cssClass property (which is stored in the private field this._class). Two handlers are added to the new span: one for click and one for focus and both point to the same method, this.beginEdit. The way we?re creating the events is by using the very handy addHandlers method, which will attach several event handlers at once and will also take care of creating delegates with the specified context (this) so that the handler can have access to all the members of the behavior instance. Finally, the new span is added to the DOM right before the input element. Once that span has been created, we can store the old display mode of the input for later use, create a blur delegate for the input element that will call endEdit and then call endEdit to substitute the input for the span. Notice that we created the delegate manually rather than use addHandlers here. I?ll explain why in a moment.
A good practice is to free all the resources and clean up after yourself from dispose. Let?s look how this is done:
dispose: function() { if (this._span) { Sys.UI.DomEvent.clearHandlers(this._span); this._span.parentNode.removeChild(this._span); var elt = this.get_element(); Sys.UI.DomEvent.removeHandler(elt, "blur", this._inputBlur); elt.style.display = this._oldDisplay; this._inputBlur = this._spanClick = this._span = null; } Bleroy.Sample.EditInPlace.callBaseMethod(this, "dispose"); }
Here we made sure that the method can be called multiple times by testing for the presence of what we?re going to clean. If the method is ever called again it will pretty much be a no-op.
The dispose method starts by clearing all event handlers from the span element. It can do so by calling the handy clearHandlers method. We can afford to do that here because we completely own that element: we created it and managed it so we can safely use a very big hammer and crush all events without having to wonder if somebody else left their fingers on the table. For the input element that is quite different as there may be other behaviors attached to it that may have created their own events. That?s why here we?re targeting our cleaning efforts exclusively at what we created. This is why we created the delegate to endEdit ourselves so that we could keep a reference to it and use that later when removing the handler.
Other tasks in dispose include removing the span from the DOM, resetting the input element?s display style and clearing private variables.
And of course, the last thing we do is call base so that we can do our cleaning up without having to wonder if base already destroyed some of the objects we still need.
Now that the initial setup and cleanup are in place, beginEdit and endEdit only have some trivial showing, hiding and focusing of the elements to do:
beginEdit: function() { /// <summary>Puts the behavior in edit mode</summary> var elt = this.get_element(); this._span.style.display = "none"; elt.style.display = this._oldDisplay; elt.focus(); this.raisePropertyChanged("isEditing"); } endEdit: function() { /// <summary>Puts the behavior out of edit mode</summary> var elt = this.get_element(); this._span.innerHTML = elt.value; this._span.style.display = this._oldDisplay; elt.style.display = "none"; this.raisePropertyChanged("isEditing"); }
The only thing worth singling out here is that we?re taking care of triggering change notifications on isEditing every time the editing mode changes.
The rest of the code is pretty trivial property accessors that are implemented using the get_ and set_ prefixes.
I hope this helps.
Get the full source code for the behavior and the sample page:
http://weblogs.asp.net/blogs/bleroy/Samples/EditInPlace/EditInPlace.zip
We might need to read the contents of some page (local or remote) by code. This is quite simple in .net.
using System.Net;
using System.IO;
WebRequest req = WebRequest.Create("http://www.asp.net");
WebResponse res = req.GetResponse();
StreamReader sr = new StreamReader(res.GetResponseStream());
string html = sr.ReadToEnd();
The string html will then hold the html contents of www.asp.net. We can also use relative uris in the same website:
WebRequest req = WebRequest.Create(new Uri("somepage.aspx", UriKind.Relative));
Hope that helps.
The CollapsiblePanelExtender is a cool way to convert simple panels to collapsible ones. It usually has a target panel, a control which can make it collapse, another (or the same) control to make it expand etc. Clicking the said controls will trigger the collapse/open behaviour.
We may need to sometimes control this behaviour from code - both client side javascript and server side code behind. Suppose we have a CollapsiblePanelExtender with the Id 'cpe1'. We wish to manipulate the state via code.
Client Side
Client side manipulation is pretty simple and can be found in the tutorials section in www.asp.net/learn - we just need to call these methods:
To open: $find('cpe1')._doOpen();
To close: $find('cpe1')._doClose();
The $find tracks down the behaviour object and calls the _doOpen or _doClose methods.
Server Side
This is surprisingly slightly trickier. You'd think that just calling
ScriptManager.RegisterClientScriptBlock(this, typeof(Page), DateTime.Now.ToString(), "$find('cpe1')._doOpen();", true);
would work. It doesn't. Any attempt to call $find('cpe1') results in a null. This is likely because of the fact that behaviours are added at the end and our script gets registered before the behaviour is available. The actual way to do it is actually quite simple (once you know how):
To open: cpe1.Collapsed = true; cpe1.ClientState = "true";
To close: cpe1.Collapsed = false; cpe1.ClientState = "false";
Setting just the Collapsed property is not enough, you need to set both. It's actually a lot less messy than registering javascript, but it is very annoying until you know it.
Hope that helps.
During a debugging session today Søren and I got to talk about how the Page_Load method on a page is called.
The method is called after the Load event and is declared like this:
1: protected void Page_Load(object sender, EventArgs e)
2: {
3:
4: }
Taking a look at the Page class in Reflector shows the it inherits from the TemplateControl class. All the following code is extracted using Reflector.
If we take a look at the TemplateControl class we can see that following constants are declared:
1: private const string _onTransactionAbortEventName = "OnTransactionAbort";
2: private const string _onTransactionCommitEventName = "OnTransactionCommit";
3: private const string _pageAbortTransactionEventName = "Page_AbortTransaction";
4: private const string _pageCommitTransactionEventName = "Page_CommitTransaction";
5: private const string _pageDataBindEventName = "Page_DataBind";
6: private const string _pageErrorEventName = "Page_Error";
7: private const string _pageInitCompleteEventName = "Page_InitComplete";
8: private const string _pageInitEventName = "Page_Init";
9: private const string _pageLoadCompleteEventName = "Page_LoadComplete";
10: private const string _pageLoadEventName = "Page_Load";
11: private const string _pagePreInitEventName = "Page_PreInit";
12: private const string _pagePreLoadEventName = "Page_PreLoad";
13: private const string _pagePreRenderCompleteEventName = "Page_PreRenderComplete";
14: private const string _pagePreRenderEventName = "Page_PreRender";
15: private const string _pageSaveStateCompleteEventName = "Page_SaveStateComplete";
16: private const string _pageUnloadEventName = "Page_Unload";
Right there on line 10 you can the name of the Page_Load method declared. If you declare a method with the same signature as the default generated Page_Load method, but name them after the constants declared above they will automatically get called appropriately.
The reason why, is found in the HookUpAutomaticHandlers() method on the TemplateControl class which eventually calls the GetDelegateInformationFromMethod(string, IDictionary) method. The source looks something like this (please excuse the variable naming). Methods are listed in the order which they are called:
1: internal void HookUpAutomaticHandlers()
2: {
3: if (this.SupportAutoEvents)
4: {
5: object obj2 = _eventListCache[base.GetType()];
6: IDictionary dictionary = null;
7: if (obj2 == null)
8: {
9: lock (_lockObject)
10: {
11: obj2 = _eventListCache[base.GetType()];
12: if (obj2 == null)
13: {
14: dictionary = new ListDictionary();
15: this.GetDelegateInformation(dictionary);
16: if (dictionary.Count == 0)
17: {
18: obj2 = _emptyEventSingleton;
19: }
20: else
21: {
22: obj2 = dictionary;
23: }
24: _eventListCache[base.GetType()] = obj2;
25: }
26: }
27: }
28: if (obj2 != _emptyEventSingleton)
29: {
30: dictionary = (IDictionary) obj2;
31: foreach (string str in dictionary.Keys)
32: {
33: EventMethodInfo info = (EventMethodInfo) dictionary[str];
34: bool flag = false;
35: MethodInfo methodInfo = info.MethodInfo;
36: Delegate delegate2 = base.Events[_eventObjects[str]];
37: if (delegate2 != null)
38: {
39: foreach (Delegate delegate3 in delegate2.GetInvocationList())
40: {
41: if (delegate3.Method.Equals(methodInfo))
42: {
43: flag = true;
44: break;
45: }
46: }
47: }
48: if (!flag)
49: {
50: IntPtr functionPointer = methodInfo.MethodHandle.GetFunctionPointer();
51: EventHandler handler = new CalliEventHandlerDelegateProxy(this, functionPointer, info.IsArgless).Handler;
52: base.Events.AddHandler(_eventObjects[str], handler);
53: }
54: }
55: }
56: }
57: }
58:
59: private void GetDelegateInformation(IDictionary dictionary)
60: {
61: if (HttpRuntime.IsFullTrust)
62: {
63: this.GetDelegateInformationWithNoAssert(dictionary);
64: }
65: else
66: {
67: this.GetDelegateInformationWithAssert(dictionary);
68: }
69: }
70:
71: private void GetDelegateInformationWithNoAssert(IDictionary dictionary)
72: {
73: if (this is Page)
74: {
75: this.GetDelegateInformationFromMethod("Page_PreInit", dictionary);
76: this.GetDelegateInformationFromMethod("Page_PreLoad", dictionary);
77: this.GetDelegateInformationFromMethod("Page_LoadComplete", dictionary);
78: this.GetDelegateInformationFromMethod("Page_PreRenderComplete", dictionary);
79: this.GetDelegateInformationFromMethod("Page_InitComplete", dictionary);
80: this.GetDelegateInformationFromMethod("Page_SaveStateComplete", dictionary);
81: }
82: this.GetDelegateInformationFromMethod("Page_Init", dictionary);
83: this.GetDelegateInformationFromMethod("Page_Load", dictionary);
84: this.GetDelegateInformationFromMethod("Page_DataBind", dictionary);
85: this.GetDelegateInformationFromMethod("Page_PreRender", dictionary);
86: this.GetDelegateInformationFromMethod("Page_Unload", dictionary);
87: this.GetDelegateInformationFromMethod("Page_Error", dictionary);
88: if (!this.GetDelegateInformationFromMethod("Page_AbortTransaction", dictionary))
89: {
90: this.GetDelegateInformationFromMethod("OnTransactionAbort", dictionary);
91: }
92: if (!this.GetDelegateInformationFromMethod("Page_CommitTransaction", dictionary))
93: {
94: this.GetDelegateInformationFromMethod("OnTransactionCommit", dictionary);
95: }
96: }
97:
98: private bool GetDelegateInformationFromMethod(string methodName, IDictionary dictionary)
99: {
100: EventHandler handler = (EventHandler) Delegate.CreateDelegate(typeof(EventHandler), this, methodName, true, false);
101: if (handler != null)
102: {
103: dictionary[methodName] = new EventMethodInfo(handler.Method, false);
104: return true;
105: }
106: VoidMethod method = (VoidMethod) Delegate.CreateDelegate(typeof(VoidMethod), this, methodName, true, false);
107: if (method != null)
108: {
109: dictionary[methodName] = new EventMethodInfo(method.Method, true);
110: return true;
111: }
112: return false;
113: }
On line 15 the delegates are all collected into a dictionary using the GetDelegateInformationFromMethod method. On lines 31 to 53 the MethodInfos are collected, and handlers is added to the events collection. When raising the associated events (e.g. Load for the Page_Load method), the MethodInfo is invoked from the Events-collection.
The HookUpAutomaticHandlers method is called from the SetIntrinsics method on the Page class.
So to sum up, the Page_Load method and all it's colleagues (Page_Init, Page_DataBind etc.) are not invoked using slow reflection, but is simply grabbed as a named delegate. I'm not used to use these delegates, but from now on I don't see any reason why not to use them, instead of overriding the OnInit, OnPreRender etc. methods.
Well, nothing new in this one, just stuff thats makes me wonder :o)
Today I came across and interesting WCF problem. A dev in our team added a new method to a WCF service and tried to update the service reference from the client app but got this error:

The error message is very helpful because you can figure out from it that something is now larger then a default value, that something being the SOAP message sent by the metadata exchange binding. Oh that?s easy to fix, just put bigger values in the config file right? Wrong because the mexBinding doesn?t provide any properties to change these values.
A quick search lead me to this great post :
http://geekswithblogs.net/claraoscura/archive/2007/08/20/114806.aspx
Since you can?t change the default values, you need to use a different binding type and you also need to up these values in the Svcutil config file that Visual Studio uses to generate/update the service reference.
Why is the max value so low (16384)? No idea.
Ok, so Microsoft's Team Foundation Server has been available for several years. We've stuck with a couple of open source source control / project management apps. John keeps moving things around and trying new stuff. I've just stuck with the evil SourceSafe because it was simple and it worked and as long it was just me working on something, I've survived. No more, I need to move out into the world. I have been working to setup TFS 2008 on Windows 2008 Server with Sql Server 2008, but I found out later that was a no go without SP1 of TFS. I went back to t Windows 2003 with Sql Server 2005. It installed and worked pretty easily. Mickey Goussett andwered a few dumb questions for me. This morning, I found one final problem. I had everything setup, but I was getting this weird error about not being able to connect to the Sql Server Reporting Services. WTF is that all about. In reading through the error log, it dawned on me that the client was trying to connect using the SMB name of the server even though I was trying to connect over ip. I set an entry for the server name in my host file and it all seems to be working now. To make it even better, I have the server running in a virtual server session. If necessary, I can move the server to whereever I want with just a few hours of notice. Very nice indeed. I'm trying the TFS thing. I hope it all works out!