Creating a Web Messenger control from Scratch – Part 8

So far in this series we have learned about the Sign-In control, how to use it and the new features allowing you customize and hide it. We have also learned about the User class, how to implement that and also some of it’s new features such as the ability to display the user’s sign-in picture. The next step in our journey through the Web Messenger API and our Custom Control is contacts. After all there is little point in having a messenger control if you have nobody to talk to.

In Windows Live Messenger, there is the ability to group your contacts. You can place each contact into a group so that referencing a contact becomes quicker and Messenger is easier to manage. So before we delve into Contacts themselves, we need to implement groups.

A users personal list of groups obviously don’t become available until after they have signed in, therefore we will create a new function that retrieves and displays the list of the user’s groups. As you may have guessed, because there can be multiple groups, there are two classes that we are interested in. The Group Collection class which holds a collection of Group objects.

The group collection class :-

messgroupcollectionmethods_thumb_71cc8837 Developer

  • Contains – Determines whether the object is within the collection.
  • Create – Creates a new group.
  • GetEnumerator – Gets the collection enumerator.
  • GetType – Gets the Type of the current instance.
  • IsNameValid – Determines whether the provided group name is valid.
  • OnCollectionChanged – Raises the CollectionChanged event.
  • OnPropertyChanged – Raises the PropertyChanged event.
  • Remove – Removes a group from the collection.

There are a few interesting methods here that we can use but for now all that we’re interested in is the GetEnumerator method as we will use this to loop through the group collection.

function displayContacts()
{
    var enumgroups = msgruser.get_groups().getEnumerator();
//    var groupindex = 0;
//    index = 0;
    while (enumgroups.moveNext())
    {
        var currentGroup = enumgroups.get_current();
        var name = currentGroup.get_name();
        var group = new Messenger.Group(name);
        group.DisplayGroup();
//        groupindex++;
    }
}

 

Above is our displayContacts function that we will be expanding on over the next couple of tutorials. For now it is fairly simple and straightforward. The first thing we do is to call the GetEnumerator method of the groups collection returned from the User class. Next we simply loop through this collection and instantiate a new custom Messenger.Group object, one for every group in the list. As a constructor for this object you pass in the group name which is a property of the Group class (not the group collection class) :-

messgroupproperties_thumb_740910f3 Developer

  • Contacts – Gets the ContactCollection that contains the contacts in the group.
  • Name – Gets or sets the name of the group.

The group class that I’ve created is just a standard Asp.Net Ajax class that will also get expanded upon later, but nicely encapsulates what we need to do for a group (over the past week I’ve been cleaning up some of the code we have already talked about and moving it to classes however there is no real need to do so).

Type.registerNamespace("Messenger");
 
Messenger.Group = function(GroupName)
{
    this.GroupName = GroupName;
    this.ShowGroup = false;
}

 

 

Here we create the Group class and put it in the Messenger namespace that we’ve been using so far. As you can see, the constructor for this class expects a Group Name as input. We also define a variable to hold whether the group is expanded or collapsed (which we’ll use in a minute).

Messenger.Group.prototype =
{
    get_GroupName: function()
    {
        return this.GroupName;
    },
    set_GroupName: function(value)
    {
        this.GroupName = value;
    },
    get_ShowGroup: function()
    {
        return this.ShowGroup;
    },

 

 

Above we are defining the methods, properties, events etc. that our class will hold. We create a read/write property for the GroupName variable and also a read only property for the ShowGroup variable.

DisplayGroup: function()
{
    if ($get("Group" + this.GroupName) == null)
    {
        //Get base group DOM element
        var contacts = $get("divContacts");
        var outerdiv = document.createElement("div");
        //create a link for the contact
        var link = document.createElement("a");
        //Add an event handler for when this gets clicked
        $addHandler(link, "click", this.ToggleGroup);
        link.className = "GroupLink";
        var img = new Image();
        img.src = "WebMessenger/Images/minus_icon.gif";
        img.id = "GroupExpand" + this.GroupName;
        img.className = "GroupImage";
        //Create a span to place the name of the group
        var name = document.createElement("span");
        name.innerHTML = "  " + this.GroupName;
        name.className = "GroupText";
        //create a horizontal rule to seperate groups
        var hr = document.createElement("hr");
        //Finally create another Div that all the contacts will list under
        var innerdiv = document.createElement("div");
        innerdiv.id = "Group" + this.GroupName;
        //Build the heirarchy
        link.appendChild(img);
        outerdiv.appendChild(link);
        outerdiv.appendChild(name);
        outerdiv.appendChild(hr);
        outerdiv.appendChild(innerdiv);
        contacts.appendChild(outerdiv);
    }
},

 

 

Our DisplayGroup method first checks to see whether this group is already displayed in our control, if it’s not then we need to display it. There are a number of ways that we could do this, one way that I’ve used in the past is simply to code the html into a string and use the innerHTML property of an element to display the html. This time however I’ve opted to actually create the elements through the DOM and assign classes to them so that it’s very easy to change things positioning, colors etc. through the stylesheet alone. It’s also a slightly cleaner approach.  So here we are just creating a couple of DIV elements to hold our group and ultimately our list of contacts in, a graphic so that you can expand or hide that particular groups entries, and display the name of the group itself. The handler calls an internal routine that expands or contracts the list of contacts based upon whether it is currently expanded or collapsed. A toggle function. Then we simply add these elements to the divContacts element that is part of base ascx page.

 

ToggleGroup: function(eventElement)
{
    var elmid = eventElement.target.id;
    var groupimg = $get(elmid);
    //get the group name so as to hide the innerdiv which
    //contains all the actual contacts
    var groupname = elmid.substr(11);
    var innerdiv = $get("Group" + groupname);
    if (this.ShowGroup == true)
    {
        groupimg.src = "WebMessenger/Images/minus_icon.gif";
        innerdiv.style.display = "none";
        this.ShowGroup = false;
    }
    else
    {
        groupimg.src = "WebMessenger/Images/plus_icon.gif";
        innerdiv.style.display = "block";
        this.ShowGroup = true;
    }
},

 

 

The ToggleGroup handler as mentioned uses the ShowGroup variable that we defined to determine whether this contact group is currently expanded or collapsed and expands the group if it is currently collapsed, or collapses it if it is currently expanded. We also change the image to reflect this behavior.

    RemoveChildrenFromNode: function(id)
    {
        var node = document.getElementById(id);
        if (node == undefined || node == null)
        {
            return;
        }
        var len = node.childNodes.length;
        while (node.hasChildNodes())
        {
            node.removeChild(node.firstChild);
        }
    },
    dispose: function()
    { }
}
Messenger.Group.registerClass("Messenger.Group", null, Sys.IDisposable);
if (typeof (Sys) !== "undefined")
{
    Sys.Application.notifyScriptLoaded();
}

 

 

The RemoveChildrenFromNode function I’ve covered previously. It’s the exact same and simply clears out an element of all it’s children. The rest is fairly standard Asp.Net Ajax code for registering the class.

So where do we call all of this from? As mentioned, the Group Collection and Group classes for a user are available after a user sign in. We already have a handler in our code that gets called upon from the Sign In Control when sign in completes so we simply place a call to our displayContacts function in there :-

function signInCompleted(sender, e)
    {
        if (e.get_resultCode() === Microsoft.Live.Messenger.SignInResultCode.success)
        {
            msgruser.get_presence().add_propertyChanged(Delegate.create(null, user_Presence_PropertyChanged));
            var selectStatus = $get('selectStatus');
            selectStatus.selectedIndex = 6;
            getMessengerLinks();
            //Get the users IMAddressPresence
            displayUserInfo();
 
            //Now that the user has signed in, get their contacts
            displayContacts();
 
        }
    }

 

Finally, since we have a new class (and a new stylesheet) we need to register these with the hosting page. So in our code behind for the ascx page, we add these as we did previously with our other classes and style sheets, in the Page_Load event :-

protected void Page_Load(object sender, EventArgs e)
{
    HtmlHead header = (HtmlHead)this.Page.Header;
    HtmlLink stylesheet = new HtmlLink();
    stylesheet.Attributes.Add("href", "WebMessenger/Default.css");
    stylesheet.Attributes.Add("rel", "stylesheet");
    stylesheet.Attributes.Add("type", "text/css");
    header.Controls.Add(stylesheet);
            HtmlLink UserInfoSylteSheet = new HtmlLink();
            UserInfoSylteSheet.Attributes.Add("href", "WebMessenger/UserInfo.css");
            UserInfoSylteSheet.Attributes.Add("rel", "stylesheet");
            UserInfoSylteSheet.Attributes.Add("type", "text/css");
            header.Controls.Add(UserInfoSylteSheet);
            HtmlLink GroupSylteSheet = new HtmlLink();
            GroupSylteSheet.Attributes.Add("href", "WebMessenger/Group.css");
            GroupSylteSheet.Attributes.Add("rel", "stylesheet");
            GroupSylteSheet.Attributes.Add("type", "text/css");
            header.Controls.Add(GroupSylteSheet);
 
    //Add our script classes to scriptmanager
    ScriptManager sm = ScriptManager.GetCurrent(Page);
    if (!sm.Scripts.Contains(new ScriptReference("WebMessenger/Script/MessengerUI.js")))
    {
        sm.Scripts.Add(new ScriptReference("WebMessenger/Script/MessengerUI.js"));
    }
            if (!sm.Scripts.Contains(new ScriptReference("WebMessenger/Script/MessengerUserInfo.js")))
            {
                sm.Scripts.Add(new ScriptReference("WebMessenger/Script/MessengerUserInfo.js"));
            }
            if (!sm.Scripts.Contains(new ScriptReference("WebMessenger/Script/MessengerGroups.js")))
            {
                sm.Scripts.Add(new ScriptReference("WebMessenger/Script/MessengerGroups.js"));
            }
            sm.LoadScriptsBeforeUI = true;
    //Add the messenger library to our page
    if (!Page.ClientScript.IsClientScriptIncludeRegistered("messengerlib"))
    {
        Page.ClientScript.RegisterClientScriptInclude("messengerlib", "http://settings.messenger.live.com/api/2.5/messenger.js");
    }
    //For the moment add our messenger functions as a standard Javascript file
            if (!Page.ClientScript.IsClientScriptIncludeRegistered("messengerfns"))
            {
                Page.ClientScript.RegisterClientScriptInclude("messengerfns", "WebMessenger/Script/MessengerFns.js");
            }
    //Add some startup script
    if (!Page.ClientScript.IsStartupScriptRegistered(this.GetType(), "Load"))
    {
                string str = "var msgrui; function pageLoad(sender, args)";
                str += "{msgrui = new Messenger.UI(); startMessenger(msgrui.get_SignInBackColor(), msgrui.get_SignInLinkColor());}";
        ScriptManager.RegisterStartupScript(this.Page, this.GetType(), "Load", str, true);
    }
}

 

After running the code what we end up with is a list of groups at the bottom of our control :-

messgroupslist_thumb_485846f7 Developer

In the next part of this series we will continue with the functionality of groups and adding contacts to our Custom Control.