Monday, September 27, 2010

Messenger Connect - Initializing Controls

Roadmap

This post is part of a series of related posts on how to use Windows Live Messenger Connect.

1. Messenger Connect REST Explorer for exploring Windows Live resources.
2. Messenger Connect JavaScript API for listing your contacts.
4. (this post) Messenger Connect - Initializing Controls

Overview

Comparing Control Creation
A Page Displaying Three Different Initialization Techniques

There are two concepts that are discussed in this post, how to initialize controls declaratively and programmatically and how to deal with the onload event of the App tag in a master child page scenario. The code presented here will result in a page shown in the image above.

Multiple App onLoad Handlers

Let's take the second point first since it is easier. If you download the templates that allow you to easily create a Messenger Connect web application, as discussed in previous posts in this thread, you will see that the App tag is defined in the master page (Site.Master.aspx) as something like this:


<wl:app
channel-url="<%=WebConfigurationManager.AppSettings["wl_wrap_channel_url"]%>"
callback-url="<%=WebConfigurationManager.AppSettings["wl_wrap_client_callback"]%>?<%=SessionId%>"
client-id="<%=WebConfigurationManager.AppSettings["wl_wrap_client_id"]%>"
scope="WL_Profiles.View, Messenger.SignIn"
onload="appLoaded">
</wl:app>


In the same master page you will see the JavaScript handler for the onload event:


<script type="text/javascript">
function appLoaded() {
}
</script>


The appLoaded function doesn't do anything in the code above but that's where you would put functionality. The dilemma I ran into was if in a child page (which inherits from the master page) you could put another handler to handle the onload event. You can't have more than one App tag so you can't just put another App tag on the child page with an onload attribute pointing to a local function on the page. (The App class is like a static class and you only have one instance of it in your web application.) The best solution I could come up with is to use a function that is not called out in the documentation but that you can easily see if you look through the Microsoft.Live.debug.js resource file. It is the
Microsoft.Live.Core.Internal.EventUtil
function which has the method addHandler(handlers, eventName, functionName) where handlers is the event handler list to modify, eventName is the name of the event, and functionName is the name of the function to invoke for the event. So you could do this in the child page:


Microsoft.Live.Core.Loader.onReady(function () {
Microsoft.Live.Core.Internal.EventUtil.addHandler(Microsoft.Live.App._handlers, 'onload', 'childPageAppLoaded');
});
// non-debug
// Microsoft.Live.Core.Internal.EventUtil.addHandler(Microsoft.Live.App.$1, 'onload', 'childPageAppLoaded');
});

function childPageAppLoaded(applicationLoadCompletedEventArgs) {
}
if you use the debug version of the library: loader.debug.js. If you use the loader.js (more typical scenario) _handlers becomes $1 as shown above in the code commented out.

I'm not sure this is the best solution, but it works. It feels like there should be a more elegant solution to this problem that doesn’t require using API pieces that are not documented.

You can see the contents of the Microsoft.Live.debug.js resource by setting a breakpoint in Visual Studio, say in the appLoaded function, and looking at the Script Documents node in the Solution Explorer when you reach the breakpoint. Too use Microsoft.Live.debug.js use the debug loader: http://js.live.net/4.0/loader.debug.js or else you will get Microsoft.Live.js which is minified and hard to read. See the image below for details:

Viewing Microsoft.Live.js resource
Solution Explorer During Debugging

Initializing Controls

Now on to the main point of this post, initializing controls. All UI controls in Messenger Connect can be created by specifying them in markup (declaratively) or using the $create method (programmatically). UI controls in the Microsoft.Live.UI namespace additionally have an initialize method that allows creating them in that way as well. The following working page (derives from the master page in the template) compares all three methods for the Microsoft.Live.UI.SignIn control and the Microsoft.Live.UI.Messenger.DisplayPictureControl. The result of running this page is shown in the image accompanying this post.

The code:



<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="CreatingControls.aspx.cs" Inherits="WindowsLiveWebApplicationBeta.CreatingControls" %>
<asp:Content ID="Content1" ContentPlaceHolderID="HeadContent" runat="server">
<script type="text/javascript">

Microsoft.Live.Core.Loader.onReady(function () {
Microsoft.Live.Core.Internal.EventUtil.addHandler(Microsoft.Live.App.$1, 'onload', 'childPageAppLoaded');
});

function childPageAppLoaded(applicationLoadCompletedEventArgs) {
// Create a sign-in control using $create
Microsoft.Live.Core.Loader.onReady(function () {
Microsoft.Live.Core.Loader.load(["Microsoft.Live", "Microsoft.Live.UI", "Microsoft.Ajax.Core"], function () {
$create(Microsoft.Live.UI.SignIn,
{ signedOutText: '$create: Sign In', signedInText: '$create: Sign Out' },
{ signin: signInCallbackCreate, signout: signOutCallbackCreate },
{},
$get('ControlsDiv2')
);
});
});
// Create a display-picture controls using $create.
Microsoft.Live.Core.Loader.onReady(function () {
Microsoft.Live.Core.Loader.load(["Microsoft.Live", "Microsoft.Live.UI"], function () {
$create(Microsoft.Live.UI.Messenger.DisplayPictureControl,
{ cid: '$user'},
{},
{},
$get('ControlsDiv3')
);
});
});


// Create a sign-in control using the API
Microsoft.Live.Core.Loader.onReady(function () {
Microsoft.Live.Core.Loader.load(['microsoft.live', 'microsoft.live.ui'], function () {
signInControl = new Microsoft.Live.UI.SignIn($get('ControlsDiv1'));
signInControl.set_signedInText('API: Sign Out');
signInControl.set_signedOutText('API: Sign In');
signInControl.add_signin(signInCallbackProg);
signInControl.add_signout(signOutCallbackProg);
signInControl.initialize();
isDefined = true;
});
});

}

// Callbacks for declarative control.
function SignedInCallback(signInCompletedEventArgs) {
alert('reached signInCallback');
}
function SignedOutCallback() {
}
// Callbacks for API control.
function signInCallbackProg(signInCompletedEventArgs) {
alert('reached signInCallbackProg');
}
function signOutCallbackProg() {
}

// Callbacks for API control.
function signInCallbackCreate(signInCompletedEventArgs) {
alert('reached signInCallbackCreate');
}
function signOutCallbackCreate() {
}

</script>
<style type="text/css">
div.left { float: left; width: 250px; border: 1px solid blue; min-height: 300px; padding: 5px;}
div.output {clear: both;}
ul { list-style-type: none;}
</style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<div id="welcome">
<h1>
SignIn and DisplayPicture - Different Approaches for Creating a Control</h1>
<div class="left">
<h4>Using Declarative Markup</h4>
<wl:signin onsignin="SignedInCallback" onsignout="SignedOutCallback" signedouttext="Declarative: Sign In" signedintext="Declarative: Sign Out">
</wl:signin>
<wl:display-picture cid="$user"/>
<br />
These controls were created by placing <pre>&lt;wl:signin /&gt;
&lt;wl:display-picture/&gt;</pre>in the HTML.
</div>
<div class="left">
<h4>Using Microsoft.Live.SignIn.UI API</h4>
<div id="ControlsDiv1"></div>
<br />
This was added during the application load using Microsoft.Live.UI.Signin. The controls in the
this namespace Microsoft.Live.UI have an initialize() method which allows the controls to
be created programmatically without using $create directly.
</div>
<div class="left">
<h4>Using $create.</h4>
<div id="ControlsDiv2"></div>
<div id="ControlsDiv3"></div>
<br />
These controls were added during the application load using $create (or <a href="http://msdn.microsoft.com/en-us/library/bb310863.aspx">Sys.Component.create</a>).
</div>
<div class="output"><span id="Output" runat="server" ></span></div>
</div>
</asp:Content>

No comments:

Post a Comment

All comments go through a moderation process. Even though it may not look like the comment was accepted, it probably was. Check back in a day if you asked a question. Thanks!