Vision

About client callbacks and building a people picker web part

July 20, 2007

Before ASP.NET Ajax was released, ASP.NET 2.0 offered another technology that lets client-side script communicate asynchronously with a server-side method. This technology is known as client callback technology and although it is reasonably easy to implement it does not offer a full-blown framework for implementing Ajax scenarios such as ASP.NET Ajax does.

However, there are still controls out there that are quite powerful that use client callbacks for their implementation, such as the ASP.NET 2.0 Treeview control and the SharePoint People Picker. In this blog post, we will discuss how client callbacks work and then show how to build a web part that uses the People Picker to find account names and groups.

Note: Under water, client callbacks use the XmlHttpRequest object.


The life cycle of a client callback consists of three stages:

  • A JavaScript event is fired that triggers a callback.
  • A server-side method is executed that accepts a string as a parameter and returns a string as the result.
  • A client-side JavaScript method receives the response and adjusts the user interface.

  • An ASP.NET page that wants to use client callbacks needs to implement the ICallbackEventHandler interface of the System.Web.UI namespace. This interface consists of two methods:

  • RaiseCallbackEvent(). This method accepts data passed by client-side code.
  • GetCallbackResult(). This method passes results back to a page.

  • An important limitation of client callbacks is that data is passed in the form of a string. If you want to transfer information that has a complex structure, you need to design some kind of system to serialize and deserialize data, or use an existing system such as JSON. You can find more information about JSON at http://json.org/.

    Also, you need to realize that the RaiseCallbackEvent() and GetCallbackResult() methods do not have access to controls on an ASP.NET page because these methods are called out-of-band and only a part of the page life cycle is executed. The following stages still take place during a callback:

    1. Init
    2. Load state
    3. Process postback data
    4. Load
    5. Callback event
    6. Unload

    Most browsers nowadays such as MSIE 5 and higher, Netscape 6 and higher, Safari 1.2 and higher, and FireFox support the use of the XmlHttpRequest object and thus allow you to use client callbacks. Use the Request.Browser.SupportsCallback property to determine if a browser supports client callbacks. If you want to determine if a postback is caused by a callback, use the Page.IsCallback property.

    Building a people picker web part

    The SharePoint people picker allows you to choose one or more account names and groups in a user friendly way. As stated before, the people picker is a powerful control that uses client callbacks. The people picker functionality is implemented in the PeopleEditor control, which is located in the Microsoft.SharePoint.WebControls namespace of the Microsoft.SharePoint assembly.

    Figure 1 shows that you can type an account name in the textbox of the people picker. There are two buttons on the people picker control: Check Names and Browse.

    If you click on the Check Names action you start looking for the name of a user or group. This causes a client callback. As the result, a list with matching names is shown, as you can see in Figure 2.

    The end result of this search is shown in Figure 3.

    Alternatively, if you click on the Browse button, the Select People and Groups – Webpage Dialog window appears that allows you to search for people and groups. This is shown in Figure 4.

    The PeopleEditor control has a number of properties that allow you to configure it. In the next section, we will discuss the properties that we think are most important.

    The PlaceButtonsUnderEntityEditor property decides the location of the Names and Browse buttons of the people picker. If you set it to true, the buttons are placed at the bottom right corner of the account name text box, otherwise to the right of it.

    The AllowEmpty property lets you define if the user is required to fill in a value in the people picker.

    The SelectionSet property allows you to define the set of users and groups the people picker can choose from. This set can consist of users, Active Directory distribution lists, Active Directory security groups, and SharePoint groups.

    The MultiSelect property lets you define if a user is allowed to specify multiple user accounts. The next code fragment shows how to add a people picker to a web part and how to configure it:

    objEditor = new PeopleEditor();
    objEditor.AutoPostBack = true;
    objEditor.PlaceButtonsUnderEntityEditor = true;
    objEditor.ID = "pplEditor";
    objEditor.AllowEmpty = false;
    objEditor.SelectionSet = "User,SecGroup,SPGroup";
    objEditor.MultiSelect = false;
    Controls.Add(objEditor);

    You can retrieve the values selected in the people picker by looping through the ResolvedEntities collection of the PeopleEditor object.

    string strAccountName = String.Empty;

    for (int i = 0; i < objEditor.ResolvedEntities.Count; i++)
    {
     PickerEntity objEntity = (PickerEntity) objEditor.ResolvedEntities[i];
     SPUserInfo objInfo = new SPUserInfo();
     objInfo.LoginName = objEntity.Key;
     strAccountName = objInfo.LoginName;
    }

    The following code listing shows the complete code for a web part that uses the people picker:

    using System;
    using System.Runtime.InteropServices;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Xml.Serialization;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.WebControls;
    using Microsoft.SharePoint.WebPartPages;

    namespace LoisAndClark.PeoplePickerExample
    {
     [Guid("93bc9836-7deb-4ce6-bc6d-91312563603b")]
     public class PeoplePickerExample : System.Web.UI.WebControls.WebParts.WebPart
     {
      private PeopleEditor objEditor;
      private Label lblAccount;
      private Button btnPostMe;

      public PeoplePickerExample()
      {
       this.ExportMode = WebPartExportMode.All;
      }

      protected override void CreateChildControls()
      {
       lblAccount = new Label();
       lblAccount.ID = "lblAccount";
       Controls.Add(lblAccount);

       objEditor = new PeopleEditor();
       objEditor.AutoPostBack = true;
       objEditor.PlaceButtonsUnderEntityEditor = true;
       objEditor.ID = "pplEditor";
       objEditor.AllowEmpty = false;
       objEditor.SelectionSet = "User,DL,SecGroup,SPGroup";
       objEditor.MultiSelect = false;
       Controls.Add(objEditor);

       btnPostMe = new Button();
       btnPostMe.Text = " OK ";
       btnPostMe.Click += new EventHandler(btnPostMe_Click);
       Controls.Add(btnPostMe);
      }

      private string GetAccountName()
      {
       string strAccountName = String.Empty;

       for (int i = 0; i < objEditor.ResolvedEntities.Count; i++)
       {
        PickerEntity objEntity = (PickerEntity) objEditor.ResolvedEntities[i];
        SPUserInfo objInfo = new SPUserInfo();
        objInfo.LoginName = objEntity.Key;
        strAccountName = objInfo.LoginName;
       }

       return strAccountName;
      }

      private void btnPostMe_Click(object sender, EventArgs eventArgs)
      {
       string strAccountName = GetAccountName();
       lblAccount.Text = "account name: " + strAccountName;
      }
     }
    }

    « back to overview page