Exploring Lync SDK ProposalTracker Sample: MiniProposalTracker CWE Application (Part 3 of 3)

Summary:   This article describes how to use the ProposalTracker sample application that is included with the Microsoft Lync 2010 SDK.

Applies to:   Microsoft Lync 2010 SDK | Microsoft Lync 2010

Published:   July 2011 | Provided by:   John Austin, Microsoft | About the Author

Contents

This article is the third in a three-part series of articles about how to use the ProposalTracker sample Silverlight application.

Mini ProposalTracker (MPT) is a Microsoft Silverlight browser application that runs in a Microsoft Lync 2010 Conversation Window Extension (CWE). MPT is the contextual conversation component of ProposalTracker. MPT displays contextual information for a proposal in an IM conversation started from ProposalTracker. MPT demonstrates the simplicity of application data sharing by letting any conversation participant select a chart type to be displayed concurrently in instances of the Lync 2010 CWE.

For information about starting a contextual conversation in ProposalTracker, see Exploring Lync SDK ProposalTracker Sample: Proposal Details (Part 2 of 3).

Figure 1 shows Mini ProposalTracker in the CWE of a user who started a contextual IM conversation on the proposal details page of ProposalTracker.

Figure 1. Mini ProposalTracker in CWE

Mini Proposal Tracker in CWE

Part 3 discusses the logic in MPT that lets a user share contextual application data with other users in a contextual IM conversation that uses MPT.

Mini ProposalTracker (MPT) uses the Microsoft.Lync.Model.Conversation.Conversation class, two Conversation class methods, and one Conversation event. The grid style of ProposalTracker is used again in MPT by referencing the ProposalTracker.dll for the ProposalTracker.PortletFrame class. In addition, MPT uses ProposalTracker.Converters.BooleanToVisibility to collapse or show one of three charts on the page.

MPT implements Lync contextual information handling logic by using the following Microsoft.Lync.Model.Conversation.Conversation members.

  • The Microsoft.Lync.Model.Conversation.Conversation class. Represents the contextual conversation.

  • The ConversationGetApplicationData(string) method. Gets the initial context data that is sent when the conversation is initiated. ProposalTracker sends the proposal description to MPT in this data.

  • The ConversationBeginSendContextData(string, string, string, AsyncCallback, object) method. Sends the user’s Chart Type button selection to other conversation participants. Selection is sent as additional context data and can be up to 64,000 characters long.

  • The Conversation.ContextDataReceived event. Gets the current chart type selected by another conversation participant when a Chart Type button is selected.

A Lync 2010 control in ProposalTracker can start a conversation and use Mini ProposalTracker (MPT) to communicate context in the Conversation Window Extension (CWE) because MPT is installed and registered on every computer at Contoso.com. The MPT registry key matches the MPT GUID with both an installation link and internal URL that points to the MPT. The Lync 2010 client reads this registry to obtain a list of extension applications that can be started in the CWE. ProposalTracker starts a new conversation and specifies the GUID of MPT as the extension application to start. The Lync 2010 client of the invited user is provided with the same GUID and runs MPT in its CWE when the conversation is accepted.

Note Note

AFCFD912-E1B7-4CB4-92EE-174D5E7A35DD is the GUID generated for MPT and is the registry key name.

Before MPT is deployed, a ContextPackages registration file is created. When this .reg file is run, it creates a key using the MPT GUID. The registration appears in figure 2.

Figure 2. Mini ProposalTracker Registry Entry

Mini Proposal Tracker Registry Entry

Important note Important

If you use the following code examples, ensure that you generate a new GUID that replaces the MPT GUID shown in the examples.

The following example adds a registry entry for MPT on a computer. The internal URL, external URL, and Install link values are changed for this article.

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Communicator\ContextPackages]

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\Domains\ServerName]
"file"=dword:00000002

[HKEY_CURRENT_USER\Software\Microsoft\Communicator\ContextPackages\{AFCFD912-E1B7-4CB4-92EE-174D5E7A35DD}]
"Name"="Mini Proposal Tracker"
"InternalURL"="\\\\Contoso_Server\\Users\\Public\\MiniProposalTrackerTestPage.html"
"ExternalURL"="\\\\Contoso_Server\\Users\\Public\\MiniProposalTrackerTestPage.html"
"InstallLink"="\\\\Contoso_Server\\Users\\Public\\MiniProposalTracker.reg"
"ExtensibilityWindowSize"=dword:00000000

Two namespaces are imported from ProposalTracker and declared in the XAML of the main page. The first namespace gives Mini ProposalTracker (MPT) access to the PortletFrame class that is visible in the ProposalTracker.dll assembly. The second namespace gives MPT access to the public BooleanToVisibility converter method that is referenced from the ProposalTracker.dll assembly.

    xmlns:ProposalTracker="clr-namespace:ProposalTracker.Controls;assembly=ProposalTracker"
    xmlns:Converters="clr-namespace:ProposalTracker.Converters;assembly=ProposalTracker"

The following example declares a resource that is used to convert a Boolean value to an instance of the Visibility enumeration.

    <UserControl.Resources>
        <!--Converters to change Boolean to Visibility-->
        <Converters:BooleanToVisibility
            x:Key="BooleanToVisibility"/>
    </UserControl.Resources>

The following example sets a Converter property on the pie chart image. The converter is set to the resource that is declared in the previous example. The same converter property is set on the other chart images on the main page.

<!--Pie Chart Image whose visibility is bound to the checked event of the PieChartRadioButton-->
            <Image
                x:Name="PieChartImage"
                Source="/MiniProposalTracker;component/Images/PieChart_Image.png"
                Visibility="{Binding ElementName=PieChartRadioButton, Path=IsChecked, Converter={StaticResource BooleanToVisibility}, ConverterParameter=cleared}" 
              />

The ProposalTracker PortletFrame is declared inside the MainPage.UserControl.Grid. This permits the PortletFrame to overlay the contents of the MainPage grid.

        <ProposalTracker:PortletFrame x:Name="MainPortletFrame" Grid.RowSpan="8"/>

When Lync 2010 opens a new conversation that carries the Mini ProposalTracker (MPT) GUID and initial context data, it gets the internal URL of MPT from the system registry and then starts MPT in a Conversation Window Extension (CWE) tab.

The MPT main page constructor sets the initial state of MPT by completing the following tasks:

  • Gets the hosting Conversation instance.

  • Gets the conversation subject property string value.

  • Gets the initial context data string value by calling the ConversationGetApplicationData(string) method, passing the MPT GUID in the argument.

  • Registers for the Conversation.ContextDataReceived event.

  • Sets the PortletFrame.PortletTitle property to the conversation subject value.

  • Sets the AppDataTextBlock.Text property to the value of the initial context data.

After completing these tasks, the MPT frame title is set to the name of a proposal, the description of the proposal is displayed above the default chart, and MPT is listens for additional context data sent by another conversation participant.

The following example is the constructor of the MainPage code-behind class.

    /// <summary>
    /// Class to support Contextual Conversation between two clients.
    /// </summary>
    public partial class MainPage : UserControl
    {
        //The GUID specifies the project. For a new application you should create another GUID
        //and then modify the reg file to reflect your new GUID. See SDK documentation for details.
        private const string ApplicationGuid = "{AFCFD912-E1B7-4CB4-92EE-174D5E7A35DD}";
        private readonly Conversation _conversation;
        private readonly String _subject;
        private readonly String _appData;

        public MainPage()
        {
            InitializeComponent();

            //Get the conversation from the Lync client
            try
            {
                _conversation = (Conversation)LyncClient.GetHostingConversation();
            }
            catch (LyncClientException lyncClientException)
            {
                Debug.WriteLine(lyncClientException);
            }
            catch (SystemException systemException)
            {
                if (IsLyncException(systemException))
                {
                    // Log the exception thrown by the Lync Model API.
                    Console.WriteLine("Error: " + systemException);
                }
                else
                {
                    // Rethrow the SystemException which did not come from the Lync Model API.
                    throw;
                }
            }

            if (_conversation != null)
            {
                //Get the subject and appdata from the conversation. In this application they represent the
                //Proposals ProjectName and Description respectively.
                try
                {
                    _subject = (string)_conversation.Properties[ConversationProperty.Subject];
                    _appData = _conversation.GetApplicationData(ApplicationGuid);
                }
                catch (LyncClientException lyncClientException)
                {
                    Debug.WriteLine(lyncClientException);
                }
                catch (SystemException systemException)
                {
                    if (IsLyncException(systemException))
                    {
                        // Log the exception thrown by the Lync Model API.
                        Console.WriteLine("Error: " + systemException);
                    }
                    else
                    {
                        // Rethrow the SystemException which did not come from the Lync Model API.
                        throw;
                    }
                }

                //Event to get fired when a context is received from other participants.
                _conversation.ContextDataReceived += ConversationContextDataReceived;
                
                //By default, we want one of the checkboxes to get checked when the context loads.
                PieChartRadioButton.IsChecked = true;

                //Bind the subject to be the title and the app data to be the description.
                MainPortletFrame.PortletTitle = _subject;
                AppDataTextBlock.Text = _appData;
            }
        }


Important noteImportant

The Conversation.ContextDataReceived event is registered on the conversation object that hosts an extension application instead of on the extension application object. Consequently, if contextual data is sent to the conversation and is designated for one of the hosted extension applications, all of the hosted applications handle the event. Only the hosted application that has a matching GUID should consume the contextual data.

A single Lync 2010 conversation window can host up to three unique extension applications concurrently. Any of these extension applications can send contextual data and the hosting conversation is notified when contextual data is received for any of its hosted extension applications.

When another conversation participant sends contextual data from Mini ProposalTracker (MPT), the MPT GUID must be sent together with the data. To verify that contextual data that is received by the local user’s conversation object is intended for MPT, MPT compares the ContextEventArgs.ApplicationId property in the contextual data received event to its own GUID. If the two GUID values do not match, the contextual data is ignored.

The following example declares a const string that is filled with the GUID that identifies MPT. This GUID is used by MPT to specify application-specific context data that is sent and received.

        //The GUID specifies the project. For a new application you should create another GUID
        //and then modify the reg file to reflect your new GUID. See SDK documentation for details.
        private const string ApplicationGuid = "{AFCFD912-E1B7-4CB4-92EE-174D5E7A35DD}";

Important noteImportant

Lync lets you send context data in either a 2,000 character block or a 64,000 character block. The 2,000 character context message is known as initial context and can be sent one or more times. You get the initial context data sent to you by calling the ConversationGetApplicationData(string) method. GetApplicationData returns the most recent initial context data sent.

The 64,000 character block is known as additional context data and can be sent one or more times. Additional context data is not cached. You can only get additional context data if you handle the Conversation.ContextDataReceived event and read the ContextEventArgs.ContextData property returned in the Conversation.ContextDataReceived event.

Sending Contextual Data

The following example sends the name of the selected chart type button to other conversation participants. The Mini ProposalTracker (MPT) GUID is sent in the first argument of the ConversationBeginSendContextData(string, string, string, AsyncCallback, object) method.

        /// <summary>
        /// Method responsible for sending the name of the checked RadioButton. 
        /// The try/catch is inserted to make sure that a friendly error message is 
        /// thrown when the context is sent to a participant that has left the conversation.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SelectChartTypeRadioButtonChecked(object sender, RoutedEventArgs e)
        {
            RadioButton checkedRadioButton = e.OriginalSource as RadioButton;
            try
            {
                if (checkedRadioButton != null)
                {
                    try
                    {
                        //Send the RadioButton name as the contextdata.
                        _conversation.BeginSendContextData(ApplicationGuid, "text/plain", checkedRadioButton.Name, null,null);
                    }
                    catch (LyncClientException lyncClientException)
                    {
                        Debug.WriteLine(lyncClientException);
                    }
                    catch (SystemException systemException)
                    {
                        if (IsLyncException(systemException))
                        {
                            // Log the exception thrown by the Lync Model API.
                            Console.WriteLine("Error: " + systemException);
                        }
                        else
                        {
                            // Rethrow the SystemException which did not come from the Lync Model API.
                            throw;
                        }
                    }

                    //If the context data has been sent, make the error text block invisible.
                    ErrorTextBlock.Visibility = Visibility.Collapsed;
                }
            }
            catch (Exception exception)
            {
                ErrorTextBlock.Text =
                    string.Format(
                        "Your context data was not sent. Please verify that there is at least one participant connected. Exception: {0}",
                        exception.Message);
                ErrorTextBlock.Visibility = Visibility.Visible;
            }
        }


Receiving Contextual Data

When additional context data is sent by another conversation participant, you get that additional data by handling the Conversation.ContextDataReceived event. Compare the ContextEventArgs.ApplicationId property of the context event state object to the GUID of the extension application that should consume the context data. If the values do not match then the context data must be ignored.

The following example is invoked when the conversation has received additional context data.

        /// <summary>
        /// Event fired when a context data is received.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ConversationContextDataReceived(object sender, ContextEventArgs e)
        {
            if (e.ApplicationId == ApplicationGuid)
            {
                //Find the RadioButton (using its received name string) and make it checked. 
                //The Chart images' visibility property is bound to each RadioButton's checked property. Check MainPage.xaml for details.
                RadioButton receivingRadioButton = FindName(e.ContextData) as RadioButton;
                if (receivingRadioButton != null)
                {
                    receivingRadioButton.IsChecked = true;
                }
            }
        }

The previous examples catch either Microsoft.Lync.Model.LyncClientException or System.SystemException. If a system exception is caught, it may be one of a set of ten Lync 2010 API-related exceptions. When the caught exception is Lync 2010 API-related, Mini ProposalTracker (MPT) writes the exception message to the console. Otherwise, the exception is thrown to calling code.

The following example takes a system exception and compares it to ten Lync 2010 API-related exception types. If the system exception is of one of these types, true is returned to calling code.

        /// <summary>
        /// Identify if a particular SystemException is one of the exceptions which may be thrown
        /// by the Lync Model API.
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private bool IsLyncException(SystemException ex)
        {
            return
                ex is NotImplementedException ||
                ex is ArgumentException ||
                ex is NullReferenceException ||
                ex is NotSupportedException ||
                ex is ArgumentOutOfRangeException ||
                ex is IndexOutOfRangeException ||
                ex is InvalidOperationException ||
                ex is TypeLoadException ||
                ex is TypeInitializationException ||
                ex is InvalidCastException;
        }


The Mini ProposalTracker (MPT) application can be used to pass contextual data between Conversation Window Extension (CWE) applications.

  • You can use the code patterns in this article to create CWE extension applications.

  • You can use the 64,000 character block of the additional context data message to send objects such as serialized images, spreadsheets, or documents.

  • You can send multiple context data blocks to build and provision a CWE client.

For more information, see the following resources:

John Austin, Microsoft, is a programmer/writer in the Lync client SDK documentation team. He has been writing Microsoft technical documentation for four years. Prior to working for Microsoft, John spent two decades as a software developer.

Show: