Thursday, March 24, 2011

IMNRC in UpdatePanel - Postback Issue Resolved

Issue Premise
When using IMNRC within an Ajax based UpdatePanel, the Communicator/Lync presence appears fine upon initial load. Once a post back occurs, the presence indicators render as offline images and the communication menu is not available. This is the exact issue discussed in the Microsoft forums here.

Scenario
I created a tabbed version of the Smart Part web part. This tabbed version renders various configurable tabs, and just like the Smart Part, loads the selected aspx user control. I use an UpdatePanel to load the controls and perform partial post backs when a different tab is selected.

One of the controls I created for use within this tabbed web part is a list of users based on certain filters or criteria. I generate the users using the presence image calling the IMNRC javascript function within the onload parameter. As described in the Issue Premise, upon initial rendering, the indicators function normally. When I click on a different tab and then come back to the list of users, all of the images go "offline" and there is no more drop-down menu available.

Root Cause
The root cause lies within the IMNRC function during a postback. The objects are not set properly for conditions to render the proper code. The orginal poster of the problem found that commenting out a condition in the IMNRC function allowed the presence to persist upon postbacks. I was able to reproduce his solution and it did work but I noticed that even though the indicators showed the users' status, I no longer had the communicator menu after a postback. So the loss of functionality as well as the editing of system javascript would not work for me.

Solution
My solution involved four steps:

1) Copying and tweaking the IMNRC function to handle postbacks
2) Renaming the IMNRC function to something else (e.g. CustomIMNRC)
3) Including the new function in my control markup or including in a referenced custom .js file
4) Modifying the onload parameter of the img tag to use the new function name.

The tweaking of the IMNRC function involves creating a new variable named postback and setting that to true:

var objSpan = obj;
var id = obj.id;
var fFirst = false;
var postback = true;


Next change the fFirst variable to postback in one of the lower if conditions:

if (postback && EnsureIMNControl() && IMNControlObj.PresenceEnabled) {
var state = 1, img;
state = IMNControlObj.GetStatus(name, id);

....
Finally add || postback to the last if (fFirst) condition: {this block of code allows the communicator menu to appear over the image when moused over}

if (fFirst || postback) {
var objRet = IMNGetOOUILocation(obj);
objSpan = objRet.objSpan;

...
The full function is show below: (use it in as script in your markup or include in a reference .js file)

function CustomIMNRC(name, elem) {
        if (name == null || name == '')
            return;
        if (browseris.ie5up && browseris.win32) {
            var obj = (elem) ? elem : window.event.srcElement;
            var objSpan = obj;
            var id = obj.id;
            var fFirst = false;
            var postback = true;
            if (!IMNDictionaryObj) {
                IMNDictionaryObj = new Object();
                IMNNameDictionaryObj = new Object();
                IMNSortableObj = new Object();
                IMNShowOfflineObj = new Object();
                if (!IMNOrigScrollFunc) {
                    IMNOrigScrollFunc = window.onscroll;
                    window.onscroll = IMNScroll;
                }
            }
            if (IMNDictionaryObj) {
                if (!IMNNameDictionaryObj[id]) {
                    IMNNameDictionaryObj[id] = name;
                    fFirst = true;
                }
                if (typeof (IMNDictionaryObj[id]) == "undefined") {
                    IMNDictionaryObj[id] = 1;
                }
                if (!IMNSortableObj[id] &&
    (typeof (obj.Sortable) != "undefined")) {
                    IMNSortableObj[id] = obj.Sortable;
                    if (!bIMNOnloadAttached) {
                        if (EnsureIMNControl() && IMNControlObj.PresenceEnabled)
                            window.attachEvent("onload", IMNSortTable);
                        bIMNOnloadAttached = true;
                    }
                }
                if (!IMNShowOfflineObj[id] &&
    (typeof (obj.ShowOfflinePawn) != "undefined")) {
                    IMNShowOfflineObj[id] = obj.ShowOfflinePawn;
                }
                if (postback && EnsureIMNControl() && IMNControlObj.PresenceEnabled) {
                    var state = 1, img;
                    state = IMNControlObj.GetStatus(name, id);
                    if (IMNIsOnlineState(state) || IMNSortableObj[id] ||
     IMNShowOfflineObj[id]) {
                        img = IMNGetStatusImage(state, IMNSortableObj[id] ||
           IMNShowOfflineObj[id]);
                        IMNUpdateImage(id, img);
                        IMNDictionaryObj[id] = state;
                    }
                }
            }
            if (fFirst || postback) {
                var objRet = IMNGetOOUILocation(obj);
                objSpan = objRet.objSpan;
                if (objSpan) {
                    objSpan.onmouseover = IMNShowOOUIMouse;
                    objSpan.onfocusin = IMNShowOOUIKyb;
                    objSpan.onmouseout = IMNHideOOUI;
                    objSpan.onfocusout = IMNHideOOUI;
                }
            }
        }
    }


If you found this useful, please help support my SharePoint and .NET user group (Philly SNUG) by clicking on the logo below.

16 comments:

  1. Is the postback variable ever set to false in your code? Otherwise, the variable is not needed.

    In that case you just need to remove the conditional if(fFirst || postback), as the postback condition is always true.

    ReplyDelete
  2. Thanks Augusto! Yes I realized that but it was one of those things where after I got something to work I didn't want to change anything. Plus I wasn't sure if I needed to go back and possibly use that variable for specific cases.

    ReplyDelete
  3. Thanks for the post. It ws helpful. But I am using a Telerik RadGrid with update panel. And WE have 1 column which displays the Names with presence indicator. On PArtial post back even with the custom function it didnt work. Then I commented out if (!IMNNameDictionaryObj[id]) {
    this line and it worked fine. Please update if you have any comments on this.

    ReplyDelete
  4. my initial onload still shows presence as offline but when i hover i can see the actual presence.

    ReplyDelete
  5. the initial load should show fine. the issue i had was on a postback. is your control within an update panel?

    ReplyDelete
  6. no update panel. just an htm file with javascript. I really appreciate your help.

    ReplyDelete
  7. Zip up the files and send them to steve@stevethemanmann.com. I'll take a look. You are running this in SharePoint correct?

    ReplyDelete
  8. it's a standalone page on a regular server.

    ReplyDelete
  9. I received the files and I will take a look. Since it is not in SharePoint there is probably something missing.

    ReplyDelete
  10. thanks again for looking at the files. did i miss anything?

    ReplyDelete
  11. I haven't had a chance to look into your files yet. I will investigate asap.

    ReplyDelete
  12. Hello Steve.

    I'm trying out your code, it works fine before the postback, showing who's online or what not. But after the postback, it stops working, just as it was with the native function, any ideas? Thanks a lot.

    ReplyDelete
    Replies
    1. What browser and version are you using? Try changing the compatibility modes and see if that makes a difference.

      Delete
  13. Hi..the script CustomIMNRC is having issues while i was using in IE6 browser ; the browser window closed as soon is opened..Any help?

    ReplyDelete
    Replies
    1. I'm not sure what would cause that and I don't have IE6 to even attempt to duplicate. Maybe the postback is causing the issue?

      Delete
  14. Sorry to say it but thios all seems not necessary at all.
    Just insert these few lines into your page and everythings works at least for me.

    function EndRequestHandler(sender, args) {
    ProcessImn();
    }

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

    ReplyDelete

Matched Content