Damian Mehers' Blog Xamarin from Geneva, Switzerland.

22Feb/0717

Dealing with “Assembly x contains a Web resource with name x, but does not contain an embedded resource with name x. “

I got the go-ahead to move the ListSearchExtender from the prototype branch of the AJAX Control Toolkit to the development branch, and everything was going swimmingly until I got this error when running:
Assembly 'AjaxControlToolkit, Version=1.0.10201.0, Culture=neutral, PublicKeyToken=28f01b0e84b6d53e' contains a Web resource with name 'AjaxControlToolkit.ListSearchBehavior.js', but does not contain an embedded resource with name 'AjaxControlToolkit.ListSearchBehavior.js'.

I checked, and yes my ListSearchBehavior.js file did have its Build Action property set to Embedded Resource so the solution wasn't that easy.

Next I fired up the ildasm tool that comes with the .NET SDK and double-clicked on the manifest. Here I got a clue as to what was happening. It showed that the embedded resource was called AjaxControlToolkit.ListSearchBehavior.ListSearchBehavior.js whereas all the others were called things like AjaxControlToolkit.DropShadow.DropShadowBehavior.js

I went digging in my code and sure enough, I'd screwed up my assembly and ClientScriptResource attributes. This is the corrected version (the bold bits used to say ListSearchBehavior):

[assembly: WebResource("AjaxControlToolkit.ListSearch.ListSearchBehavior.js", "text/javascript")]
namespace AjaxControlToolkit
{
...
[ClientScriptResource("AjaxControlToolkit.ListSearchBehavior", "AjaxControlToolkit.ListSearch.ListSearchBehavior.js")]
...
public class ListSearchExtender : ExtenderControlBase

Hopefully this post will save others that are banging their heads against the wall with the same error.

Filed under: AJAX 17 Comments
21Feb/072

Initializing PopupBehavior in your AJAX Control Extender initialize method won’t work

In the ListSearchExtender which I am currently working to contribute to the AJAX Control Toolkit I use the PopupBehavior to display a DIV next to the ListBox (rendered as a SELECT) which initially has a message such as "Type to search...".  This is then replaced as the user types characters, with the characters they have typed.

If the user sets the ListBox to have the default focus (via the form's defaultFocus property) then I want to show the Prompt DIV immediately, which I initially tried doing in my control extender's initialize method.  This didn't work, and the reason is that there is a second initialization pass that takes place for the PopupBehavior, that is called by the framework, which hides the element (my DIV) associated with the PopupBehavior.  This second pass takes place after my extender's initialize method is called.

The workaround I used was to register a method to be called after all controls are initialized.  That would be the Sys.Application.load method:

this._applicationLoadDelegate = Function.createDelegate(this, this._onApplicationLoad);
Sys.Application.add_load(
this._applicationLoadDelegate);


My delegate gets called after all scripts have been loaded and the objects in the application have been created and initialized.

Filed under: AJAX 2 Comments
12Feb/073

Article: Cross-browser ASP.NET AJAX Control Extender support – trickier than I thought

I've just had an article on DotNetSlackers published on porting ASP.NET AJAX Control Extenders to multiple browsers.

Porting a control extender that I am working to contribute to the ASP.NET AJAX Control Toolkit turned out to far more involved than I initially thought it would be. If you are a hardcore JavaScript developer then a lot of this may be familiar. But if you are like me, and JavaScript is just one of many technologies you are using, read on to learn from my mistakes.

More at:

http://dotnetslackers.com/articles/ajax/CrossBrowserAjaxControlExtenderTips.aspx

Filed under: AJAX 3 Comments
24Jan/071

Geneva Presentation on 20th Feb: Enhance Your Web Applications by Using and Building ASP.NET AJAX Control Extenders

I’ll be presenting in Geneva, Switzerland at 7:00pm on 20th Feb:

In a presentation consisting almost entirely of live demonstrations, you’ll not only see how you can easily use Microsoft’s AJAX Control Extenders to make your web sites more responsive and interactive, but also how you can package up your existing JavaScript into reusable control extenders. This is not just about eye-candy, but rather about making your web applications easier and quicker to use.

Full details here: http://www.dotmugs.ch/events/event.aspx?eid=53

Anyone can attend, at no charge.  This is an update of a well received similar presentation I delivered in Zurich last year.

Damian

Filed under: AJAX 1 Comment
19Dec/061

Article: The UpdatePanel opened: what happens behind the scenes?

I've just had an article published on DotNetSlackers about what happens behind the UpdatePanel.

"The UpdatePanel is one of the coolest features of Microsoft's ASP.NET AJAX. It magically lets you enhance your web sites with AJAX goodness, with very little cost to you as a developer. If you are like me though, you'll be feeling just a little bit uncomfortable simply dragging the UpdatePanel onto your web form, and then letting it do its stuff. We want to understand what it is doing on our behalf.

In this article I'll walk through what happens in the browser when an UpdatePanel updates. You'll see what events are fired, what intervention points there are where you can intercept the action, and generally get a feel for the sequence of events."

More at http://dotnetslackers.com/articles/atlas/The_UpdatePanel_opened_what_happens_behind_the_scenes.aspx

Filed under: AJAX 1 Comment
19Dec/060

Uploaded presentation: Enhance Your Web Applications with AJAX Control Extenders

The presentation I gave last night in Zurich on ASP.NET AJAX and Control extenders on was very well attended,  -- some great feedback too.  Thank you to all those that turned out so close to Christmas!

Atif gave a very good presentation before mine, on REST, JSON, Jayrock and more.  Some thought-provoking stuff. 

Many thanks also to Atif for organizing the event, and Digicomp/Matt Fasola for hosting it.

My presentation can be downloaded here.  The real content was in the demos though.

Filed under: AJAX No Comments
6Dec/060

Presentation for Swiss .NET group on 18th Dec 2006: Enhance Your Web Applications by Using and Building ASP.NET AJAX Control Extenders

I'll be presenting in Zurich, Switzerland at 7:30pm on 18th Dec:

In a presentation consisting almost entirely of live demonstrations, you'll not only see how you can easily use Microsoft's AJAX Control Extenders to make your web sites more responsive and interactive, but also how you can package up your existing JavaScript into reusable control extenders. This is not just about eye-candy, but rather about making your web applications easier and quicker to use.

Full details here: http://www.dotmugs.ch/events/event.aspx?eid=50

Anyone can attend, at no charge.

Damian

Filed under: AJAX No Comments
27Nov/066

Tip/Trick: Using ScriptManager ScriptPath to load MicrosoftAjax.js from file system

This isn't particularly tricky but it can be non-obvious.

If you don't want to use the embedded resources to serve the Microsoft AJAX JavaScript runtime, but instead you want to have them served from the file system, to make JavaScript debugging more pleasant, then this is what you need to do:

Create a directory structure under your web application's root directory

You'll need to create a directory structure that corresponds to the attributes of the assembly that normally contains the script files.  In your case you'll can create a subdirectory structure in your web site like this:

Scripts\Microsoft.Web.Extensions\1.0.61025.0

The top directory doesn't have to be called Scripts you can call it anything you wish.  Since MicrosoftAjax.js is in the Microsoft.Web.Extensions assembly, this is why the second directory level is called Microsoft.Web.Extensions.  The final directory is the version number of the DLL.  You can see this from your web.config:

<compilation debug="false">
  <assemblies>
     <add assembly="Microsoft.Web.Extensions, Version=1.0.61025.0, ..."/>
  </assemblies>
</compilation>

Copy the ASP.NET AJAX JavaScript runtime files

When you installed ASP.NET AJAX you'll have had the ASP.NET AJAX JavaScript source files installed, by default into:

C:\Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025\ScriptLibrary\Debug

and

C:\Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025\ScriptLibrary\Release

Copy MicrosoftAjax.js from the Debug directory into your Scripts\Microsoft.Web.Extensions\1.0.61025.0 directory, and rename it to Microsoft.Web.Resources.ScriptLibrary.MicrosoftAjax.debug.js.  Then do the same for MicrosoftAjaxTimer.js and MicrosoftAjaxWebForms.js following the same pattern of adding "Microsoft.Web.Resources.ScriptLibrary." to the start of the filename, and then adding ".debug" in front of the ".js".

Also copy the same files from the Release directory but this time rename them without adding the ".debug" in front of the ".js".

Now the source files will be picked up whether or not you are running in debug mode.

Set the ScriptPath on your ScriptManager

Finally, set the ScriptPath property on ASP.NET ScriptManager to tell it to source the ASP.NET AJAX runtime from your new directory:

<asp:ScriptManager ID="sm1" runat="server" ScriptPath="~/Scripts">
</asp:ScriptManager>

Verifying that the JavaScript files are being served from files

If you do a "View Source" in Internet Explorer you should see the JavaScript files being included from the appropriate location:

<script src="Scripts/Microsoft.Web.Extensions/1.0.61025.0/
Microsoft.Web.Resources.ScriptLibrary.MicrosoftAjax.js"
type="text/javascript"></script>
 

About Damian

Filed under: AJAX 6 Comments
23Nov/0611

Using the Active Scripting Debug API to log all JavaScript that IE executes

You can download the free TraceJS program here. Run it, select the Internet Explorer instance you are interested in, and you'll be able to see all JavaScript it executes.  If you get an error starting it up, it is likely you do not have Microsoft's Active Scripting Debug API installed.  You can download the Microsoft Script Debugger, which will install it, here

Sometimes I get the germ of an idea and I won't let go until I have implemented it. In this case I wanted to see every line of JavaScript code that was being executed inside Internet explorer. There are plenty of library routines that let you instrument your code, but I wanted something different.

I wanted to actually hook into Internet explorer somehow. A debugger API seemed like the obvious way to go. But which debugger the API? There are so many to choose from. In the end I discovered that the Active Scripting API was what I needed. Unfortunately it's been a long time since anyone has used that it API. Especially from .NET. There were a smattering of articles on the web but none did exactly what I wanted.

In order to use it from .NET I needed a type library in order to generate a COM Interop assembly. Eventually I found one in an old version of the Internet explorer SDK. Having generated to the Interop assembly I found I needed to make some corrections. So I generated an IL file corrected it and regenerated the assembly.

For a long time when I first used the API it did not seem to be working. I could not connect to Internet Explorer. The call to connect failed every time. I eventually discovered that it had actually worked the first time and that subsequent attempts to connect were failing because there was already a debugger attached. DOH!

From there on it was pretty plain sailing. As soon as I attached the debugger I requested a breakpoint. I received a call back when the breakpoint occurred and then logged all the information about the current statement including the filename, the function name, the current stack depth, the line number, and of course the current chunk of JavaScript that was being executed. This is the callback code that gets executed when a breakpoint occurs:

// Called on breakpoint, which is for each statement in the target app
public void onHandleBreakPoint(IRemoteDebugApplicationThread prpt,
                               tagBREAKREASON br,
                               IActiveScriptErrorDebug pError)
{
    try
    {
        // Properties of the current breakpoint which we will be fetching
        string fileName;
        string functionName;
        string code = "";

        // Various interfaces and structs used for active script debugging
        IEnumDebugStackFrames enumDebugStackFrames;
        tagDebugStackFrameDescriptor debugStackFrameDescriptor;
        IDebugCodeContext debugCodeContext;
        IDebugDocumentContext debugDocumentContext;
        IDebugDocument debugDocument;

        // Get the function name from the first stack frame
        prpt.EnumStackFrames(out enumDebugStackFrames);
        uint stackFrameCount;
        enumDebugStackFrames.RemoteNext(1, out debugStackFrameDescriptor,
                                out stackFrameCount);
        if (stackFrameCount != 1)
        {
            return;
        }

        debugStackFrameDescriptor.pdsf.GetDescriptionString(0,
                                                            out functionName);

        // Get the file name
        debugStackFrameDescriptor.pdsf.GetCodeContext(out debugCodeContext);
        debugCodeContext.GetDocumentContext(out debugDocumentContext);
        debugDocumentContext.GetDocument(out debugDocument);
        debugDocument.GetName(tagDOCUMENTNAMETYPE.DOCUMENTNAMETYPE_URL,
                              out fileName);

        // If we are not in the same file and function as previous
        // breakpoint, then find the stack depth
        if (fileName != lastFile || functionName != lastFunction)
        {
            stackDepth = 0;

            while (stackFrameCount == 1)
            {
                tagDebugStackFrameDescriptor tmpDebugStackFrameDescriptor;
                enumDebugStackFrames.RemoteNext(1,
                                                out tmpDebugStackFrameDescriptor,
                                                out stackFrameCount);
                stackDepth++;
            }
        }

        // The DebugDocumentText lets us get at the document text
        IDebugDocumentText ddt = debugDocument as IDebugDocumentText;
        if (ddt == null)
        {
            return;
        }

        // Find out the location of the chunk of code is currently
        // being stepped through
        uint charPosition = 0;
        uint nrChars = 0;
        ddt.GetPositionOfContext(debugDocumentContext,
                                 out charPosition,
                                 out nrChars);

        // Find out what line it is on
        uint lineNr = 0;
        uint offsetInLine;
        ddt.GetLineOfPosition(charPosition, out lineNr, out offsetInLine);

        // Get the chunk of code that is being stepped through.
        if (nrChars >= 0)
        {
            if(nrChars > codeBuffer.Length - 1) {
                nrChars = (uint)codeBuffer.Length - 1;
            }

            uint charsReturned = 0;
            ddt.GetText(charPosition, codeBufferPtr, IntPtr.Zero,
                        ref charsReturned,
                        nrChars);
            code = new String(codeBuffer, 0, (int)charsReturned);
        }

        // Tell any listeners about the new trace message
        TraceEventArgs traceEventArgs = new TraceEventArgs(fileName,
                                                           functionName,
                                                           stackDepth,
                                                           (int)lineNr,
                                                           code);
        OnTraceStep(traceEventArgs);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }
    finally
    {
        try
        {
            // Resume the process, indicating we want to break at the next available point
            debuggableApplication.RemoteDebugApplication.ResumeFromBreakPoint(prpt,
                  tagBREAKRESUME_ACTION.BREAKRESUMEACTION_STEP_INTO,
                  tagERRORRESUMEACTION.ERRORRESUMEACTION_AbortCallAndReturnErrorToCaller);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error resuming: " + ex);
        }
    }
}

Of course this has an impact on performance, but it does let you see every line of code that has run. It can be quite illuminating to trace through the Microsoft ASP.NET AJAX runtime and see exactly how much work it does on your behalf. You can also use it to see if your JavaScript code is doing what you think it is doing, without having to step through it in the debugger.

For example this is the trace from the first update panel example in the ASP.NET AJAX documentation.

I put a simple graphical interface on top of the program:

TraceJS ScreenShot

You can download the program here, and the complete source is available here.

I can imagine that this could be extended to include filtering, displaying parameters, code coverage and possibly performance metrics but as it stands I think it's already quite useful to see every line of code that has run without having to manually step through the debugger. I'm happy to contribute it to CodePlex if anyone wants to take it further.

About the author.

Filed under: AJAX 11 Comments
17Nov/064

ScreenCast: How to create and use an ASP.NET AJAX Control Extender

In this 13 minute video I walk through creating and deploying an example ASP.NET AJAX Control Extender.

The example extender automatically disables buttons when they are clicked, to stop people clicking them twice (for example on a purchase form).  Implementing this isn't as obvious as you might think, and involves cloning.

Screenshot from the video

I do absolutely everything from scratch including writing the server-side and browser-side code, and creating a web site to test the extender.

The video can be viewed/downloaded here.

Please make sure you have sound playing since I talk through a lot of what I am doing.

Filed under: AJAX 4 Comments