Share via


Building a Lync IM Conversation Window: Window Control Event Handlers (Part 2 of 5)

Summary:   This article is the second in a series of five articles that describe how to build a Microsoft Lync 2010 IM conversation window that features a spelling checker, and then add the spelling checker to the conversation window. This article describes how to develop a Lync 2010 conversation window with Microsoft Lync 2010 API, and Microsoft Windows Presentation Foundation (WPF) or Microsoft Windows Forms.

Applies to:   Microsoft Lync 2010 SDK | Microsoft Lync 2010 | Microsoft Lync 2010 API

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

Contents

  • Introduction

  • Prerequisite Concepts

  • XAML Window Definition

  • Namespaces, Delegates, and Private Fields

  • Windows Events

  • Control Events

  • Conclusion

  • Additional Resources

This article is the second in a five-part series of articles about how to build a Lync 2010 IM conversation window.

Introduction

The development scenario appearing in Building a Lync IM Conversation Window: Introduction (Part 1 of 5) describes a Microsoft Word Repository Client application feature that starts IM conversations with other document authors by only using the author’s SIP address that is provided by the repository. The SIP address associated with a document is used to get a Microsoft.Lync.Model.Contact instance that can send and receive instant messages. If a contact can send and receive instant messages, then the contact is added to a conversation that is created by the repository client. Part 2 examines the code that is used to run the IM conversation.

Each article in this series uses code from a WPF sample application that includes a conversation window, a network credential window, and the MSDNArticleIM.ClientModel and MSDNArticleIM.BingSpellChecker helper class. These classes are part of the MSDNArticleIM namespace.

Figure 1 shows the WPF conversation window that is built to work with the Word Repository Client application. The WPF conversation window is implemented by the MSDNArticleIM.ConversationWindow class.

Figure 1. WPF conversation window

Lync_IM_Article_ConversationWindow

Part 2 discusses the code-behind for each control that is used to build the conversation window. Building Lync IM Conversation Windows: Helper Methods (Part 3 of 5) discusses the helper methods that are used to start a conversation, add contacts, and check text with the spelling checker. Building Lync IM Conversation Windows: Lync 2010 API Event Handlers (Part 4 of 5) discusses the Lync 2010 API event handlers and callback methods. Building Lync IM Conversation Windows: Helper Classes (Part 5 of 5) lists the MSDNArticleIM.ClientModel helper class and shows how to add a spelling checker web service to an application.

Prerequisite Concepts

For more information about the following concepts, see Building a Lync IM Conversation Window: Introduction (Part 1 of 5).

  • Lync 2010 API classes, methods, and events that are associated with IM conversations.

  • Lync 2010 IM conversation logic.

  • Finding contacts by using the ContactManagerGetContactByUri(string), ContactManagerBeginLookup(string, AsyncCallback, object), and ContactManagerBeginSearch(string, AsyncCallback, object) methods.

  • Checking the capability of a contact to participate in IM.

The MSDNArticleIM.ConversationWindow class members include a delegate, private fields, an enumeration, helper methods, callback methods, and event handlers. the following sections discuss the class field declarations and window event handlers. Each of the examples can be inserted into the System.Windows.Window-based class that is used to build a WPF application. When you write a Windows Forms class, replace the WPF types that are used in the application together with the values that appear in the following table.

WPF types

Windows Form types

Label.Content

Label.text

Window.Title

Window.Text

System.Windows.Media.Brushes

System.Drawing.Colors

Dispacher.Invoke

Invoke

control.IsEnabled

control.Enabled

System.Windows.Controls.WebBrowser.NavigateToString(System.string)

System.Windows.Forms.WebBrowser.DocumentText

System.Windows.Threading.DispachTimer

System.Windows.Forms.Timer

XAML Window Definition

The following example declares the WPF conversation window by using an XAML notation.

<Window x:Class="MSDNArticleIM.ConversationWindow"
        xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
        Title="IM Conversation Window" Height="510" Width="571" Initialized="Window_Initialized">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="226*" />
            <ColumnDefinition Width="323*" />
        </Grid.ColumnDefinitions>
        <Label Content="Conversation with:" Height="28" HorizontalAlignment="Left" Margin="12,37,0,0" Name="label1" VerticalAlignment="Top" />
        <Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="141,37,0,0" Name="RemoteSIP_Label" VerticalAlignment="Top" />
        <TextBox Height="47" HorizontalAlignment="Left" Margin="12,93,0,0" Name="Message_TextBox" VerticalAlignment="Top" Width="525" Grid.ColumnSpan="2"  TextChanged="Message_TextBox_TextChanged" />
        <Button Content="Spell Check" Height="23" HorizontalAlignment="Left" Margin="12,156,0,0" Name="SpellCheck_Button" VerticalAlignment="Top" Width="75" Click="SpellCheck_Button_Click" />
        <Button Content="Send Message" Height="23" HorizontalAlignment="Left" Margin="222,160,0,0" Name="Send_Button" VerticalAlignment="Top" Width="89" Grid.Column="1" Click="Send_Button_Click" />
        <Label Content="Spell Check Results" Height="28" HorizontalAlignment="Left" Margin="101,155,0,0" Name="SpellCheckStatus_String" VerticalAlignment="Top" />
        <Label Content="Type a Message" Height="28" HorizontalAlignment="Left" Margin="12,58,0,0" Name="TypeMessage_Label" VerticalAlignment="Top" />
        <WebBrowser Height="203" HorizontalAlignment="Left" Margin="12,190,0,0" Name="History_WebBrowser" VerticalAlignment="Top" Width="525" Grid.ColumnSpan="2" />
        <Label Content="--" Height="28" HorizontalAlignment="Left" Margin="12,437,0,0" Name="ActivityText_Label" VerticalAlignment="Top" />
        <Button Content="End Conversation" Height="23" HorizontalAlignment="Left" Margin="207,399,0,0" Name="EndConversation_Button" VerticalAlignment="Top" Width="104" Grid.Column="1" Click="EndConversation_Button_Click" />
        <Button Content="Close" Height="23" HorizontalAlignment="Left" Margin="236,442,0,0" Name="Close_Button" VerticalAlignment="Top" Width="75" Click="Close_Button_Click" Grid.Column="1" />
        <Label Content="Remote User SIP Uri" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="RemoteUserSIP_Label" VerticalAlignment="Top" />
        <TextBox Grid.ColumnSpan="2" Height="23" HorizontalAlignment="Left" Margin="141,12,0,0" Name="SIPEntry_TextBox" VerticalAlignment="Top" Width="120" TouchLeave="SIPEntry_TextBox_TouchLeave" LostKeyboardFocus="SIPEntry_TextBox_LostKeyboardFocus" />
        <Label Content="Label" Grid.Column="1" Height="28" HorizontalAlignment="Left" Margin="49,443,0,0" Name="lblFindStatus" VerticalAlignment="Top" />
        <Label Content="Lync Status" Grid.ColumnSpan="2" Height="28" HorizontalAlignment="Left" Margin="194,442,0,0" Name="LyncStatus_Label" VerticalAlignment="Top" />
        <Button Content="Sign In" Height="23" HorizontalAlignment="Left" Margin="12,399,0,0" Name="SignIn_Button" VerticalAlignment="Top" Width="75" Click="SignIn_Button_Click" IsEnabled="False"/>
    </Grid>
</Window>

Namespaces, Delegates, and Private Fields

The following example is a part of the C# code that is used to build the conversation window for the MSDNArticleIM.ConversationWindow class, it shows the namespaces that are used and the types that are declared by the conversation window. The FormActions enumeration is used throughout the application to instruct the TakeFormAction helper method to run specific code in a switch statement.

using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Lync.Model;
using Microsoft.Lync.Model.Conversation;
using System.Collections.Generic;
using System.Text;
using System.Windows.Threading;
using System.Net;

namespace MSDNArticleIM
{

    /// <summary>
    /// Enumerates the actions that can be completed by the TakeFormAction method
    /// </summary>
    enum FormActions
    {
        CloseForm,            // Close Form.
        SetFormBackColor,     // Set the Form background color. 
        DisplayDefaultCursor, // Set the default cursor assigned to the Form.
        DisplayWaitCursor,    // Changes the mouse pointer (cursor) to an image of an hour glass shape.
        UpdateLabel,          // Updates a Label.text property.
        UpdateWindowTitle,    // Updates the window text property.
        ClearText,            // Clears the text property of a specified control.
        SetListContents,      // Set the contents of a list box.
        StartTimer,           // Starts to display composing timer.
        StopTimer,            // Stops displaying composing timer.
        DisplayTypingStatus,  // Displays the typing status of remote participants.
        EnableButton,         // Enables a button.
        DisableButton         // Disables a button.
    }

    /// <summary>
    /// Interaction logic for ConversationWindow.xaml
    /// </summary>
    public partial class ConversationWindow : Window
    {

        #region fields
        // These delegates are used by call methods on the UI thread.
        private delegate void FormActionDelegate(FormActions actionToTake, object actionObject, object actionData);

        private ClientModel _ClientModel = null;
        private Conversation _Conversation;
        private InstantMessageModality _RemoteIMModality;
        private InstantMessageModality _LocalIMModality;
        private string _targetUri;
        private Contact _remoteContact;
        private ArrayList _ConversationHistory;
        private Boolean _FormatMessageAsText = true;
        private CredentialForm _CredentialForm = null;
        private System.Windows.Window signInForm = null;
        private FormActionDelegate _FormActionDelegate;
        private StringBuilder _ConversationHistoryHtml;
        private DispatcherTimer _DispatcherTimer_DisplayComposing = new DispatcherTimer();
        #endregion

    //...

  }
}

Windows Events

The methods in this section handle the events that are raised when a user interacts with the controls on the conversation window that appears in Figure 1.

Conversation Window Initialized Event

The application uses the Initialized event to instantiate the delegate that is used throughout the class to marshal the event state from the Lync 2010 platform thread to the UI thread. In addition, it initializes and signs in to Lync 2010. The next code example runs the following tasks.

  1. Initiates the FormActionDelegate instance that is declared in the previous example.

  2. Creates the MSDNArticleIM.ClientModel helper class.

  3. Registers for an event raised by the MSDNArticleIM.ClientModel helper class after Microsoft.Lync.Model.LyncClient is initialized.

  4. Enables the Sign In button that appears in the conversation window if the user is not signed in to Lync 2010.

  5. Changes the mouse pointer (cursor) to an image of an hour glass shape.

  6. Calls the Initialize helper method on the MSDNArticleIM.ClientModel helper class.

  7. Sets the initial color, title, and label text that appears in the conversation window Form. These properties are updated automatically by application logic throughout the conversation.

  8. Registers for events on the Microsoft.Lync.Model.Conversation.ConversationManager and Microsoft.Lync.Model.LyncClient instance.

        /// <summary>
        /// Called when window is initialized 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Initialized(object sender, EventArgs e)
        {
            InitializeComponent();
            _ConversationHistory = null;
            try
            {
                //***********************************************************
                //Initializes the delegate that is responsible for all Lync 2010 API events that
                //are used to update window controls.
                //***********************************************************
                _FormActionDelegate = new FormActionDelegate(TakeFormAction);


                //***********************************************************
                //Initializes the helper class that wraps method calls and 
                //events on the Microsoft.Lync.Model.LyncClient class
                //***********************************************************
                _ClientModel = new ClientModel();

                //******************************************************************************
                //Registers for event that is raised by client model after LyncClient is initialized. 
                //******************************************************************************
                _ClientModel.ClientInitializedEvent += new ClientInitializedDelegate(_ClientModel_ClientInitializedEvent);


                //******************************************************************************
                //Enables the sign in button if the Lync 2010 instance is not signed in. 
                //******************************************************************************
                if (_ClientModel._LyncClient.State != ClientState.SignedIn)
                {
                    this.Dispatcher.Invoke(_FormActionDelegate, new object[] { FormActions.EnableButton, SignIn_Button, null });
                }

                //******************************************************************************
                //Sets the mouse pointer to an hour glass shape until Lync 2010 sign in process is complete. 
                //Pointer is set to an arrow shape in the _ClientModel_ClientInitializedEvent method that is
                //called on the Lync 2010 platform thread after the client is initialized.
                //******************************************************************************
                this.Cursor = Cursors.Wait;
                _ClientModel.InitializeClient();



                //******************************************************************************
                //Configures the initial state of the window title, color, and status label. 
                //******************************************************************************
                this.Dispatcher.Invoke(_FormActionDelegate, new object[] { FormActions.UpdateWindowTitle, this, "No Conversation" });
                this.Dispatcher.Invoke(_FormActionDelegate, new object[] { FormActions.SetFormBackColor, this, Brushes.AntiqueWhite });
                this.Dispatcher.Invoke(_FormActionDelegate, new object[] { FormActions.UpdateLabel, lblFindStatus, _ClientModel._LyncClient.State.ToString() });

                //******************************************************************************
                //Register for new conversations that are added to the conversation manager conversations collection.
                //This event is raised if a new conversation is started by the local user or
                // the local user is invited to a conversation that is started by another user.
                //******************************************************************************
                _ClientModel._LyncClient.ConversationManager.ConversationAdded += ConversationsManager_ConversationAdded;

                //******************************************************************************
                //If you have not registered for client state change events in your code, you 
                //should register here. If Lync 2010 goes offline in the middle of a conversation, you need 
                //to inform the user and programmatically react to the state change. 
                //When Lync 2010 is not signed in, all Lync 2010 API calls (except BeginSignIn) return the
                //NotSignedIn exception.
                //******************************************************************************
                _ClientModel._LyncClient.StateChanged += _LyncClient_StateChanged;
                _ClientModel._LyncClient.ClientDisconnected += _LyncClient_ClientDisconnected;
                Send_Button.Focusable = false;


                //******************************************************************************
                //Initializes the arraylist that is a: keeper of the IM text conversation history and 
                //b: the data source of the conversation list box that appears in the conversation Form.
                //******************************************************************************
                _ConversationHistory = new ArrayList();

                //******************************************************************************
                //If the conversation has not been created.
                //******************************************************************************
                if (_Conversation == null)
                {
                    //******************************************************************************
                    //Call helper method that creates a new conversation.
                    //******************************************************************************
                    Message_TextBox.Text = string.Empty;
                    _ConversationHistoryHtml = new StringBuilder();
                }

                //******************************************************************************
                //Register for the tick event on the dispatcher timer that regulates the sending
                //of the user typing status message.
                //Sets the interval between ticks at 5 seconds.
                //******************************************************************************
                _DispatcherTimer_DisplayComposing.Tick += new EventHandler(_DispatcherTimer_DisplayComposing_Tick);
                _DispatcherTimer_DisplayComposing.Interval = new TimeSpan(0, 0, 5);

                _ClientModel.ClientStateChangedEvent += new ClientStateChanged(_ClientModel_ClientStateChangedEvent);
                _ClientModel.ClientShutdownEvent += new ClientShutdown(_ClientModel_ClientShutdownEvent);

            }
            catch (ClientWrapException c)
            {
                MessageBox.Show(c.ExceptionContext, "Host not found");
                if (c.CriticalException == true)
                {
                    this.Close();
                }
            }


        }

Window_Closed Event

This event handles the event that is raised when the conversation window is closed. Events on the client model helper class, conversation, remote contact, and conversation instant message modality are unregistered. The application is designed to run a scenario where the Lync 2010 UI is suppressed. The next example shows how the application must sign out user and then end the Lync 2010 session by using the Dispose method on the MSDNArticleIM.ClientModel helper class.

        /// <summary>
        /// Handles the event that is raised when the conversation window is closed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_Closed(object sender, EventArgs e)
        {
            //********************************************************
            //If a conversation was active and a remote contact instance
            //was instantiated, then events on that remote contact are unregistered.
            //********************************************************
            if (_remoteContact != null)
            {
                _remoteContact.ContactInformationChanged -= _remoteContact_ContactInformationChanged;
            }

            //********************************************************
            //If local user InstantMessageModality instance exists, un-register for events on this modality.
            //********************************************************
            if ((InstantMessageModality)(_Conversation.Modalities[ModalityTypes.InstantMessage]) != null)
            {
                ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).InstantMessageReceived -= myInstantMessageModality_MessageReceived;
                ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).IsTypingChanged -= myInstantMessageModality_ComposingChanged;
            }

            //********************************************************
            //If remote user modality instance exists, un-register for events on the modality.
            //********************************************************
            if (_RemoteIMModality != null)
            {
                _RemoteIMModality.InstantMessageReceived -= myInstantMessageModality_MessageReceived;
                _RemoteIMModality.IsTypingChanged -= myInstantMessageModality_ComposingChanged;
            }

            _ClientModel._LyncClient.StateChanged -= _LyncClient_StateChanged;
            _ClientModel._LyncClient.ConversationManager.ConversationAdded -= ConversationsManager_ConversationAdded;

            //********************************************************
            //Un-register for events on the conversation.
            //********************************************************
            _Conversation.ParticipantAdded -= Conversation_ParticipantAdded;
            _Conversation.StateChanged -= Conversation_StateChangedEvent;

            //********************************************************
            //This helper method signs out of Lync 2010 and then ends Lync session
            //if Lync 2010 runs in UI Suppressed mode. 
            //********************************************************
            _ClientModel.Dispose();
        }

Control Events

SIPEntry_TextBox_LostKeyboardFocus Event

The following example takes the user’s input from the SIP address entry text box and sets the value of a class string field that contains the SIP address of a non-local user. If the SIP entry text field contains a “@” character or the string “conf:”, then the application code run time identifies the user as a user who has tried to enter a valid SIP address or conference URL. In this case, the application calls the helper MSDNArticleIM.ConversationWindow.StartConversation() method.

        /// <summary>
        /// Handles the event that is raised when the user moves keyboard focus off of SIP address field
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SIPEntry_TextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
        {
            
            //***********************************************************
            //Cast event originator to Textbox class.
            //***********************************************************
            TextBox entry_TextBox = (TextBox)sender;

            if (entry_TextBox.Text.Contains("@") || entry_TextBox.Text.Contains("conf:"))
            {

                //***********************************************************
                //Set value of _targetUri class string field to SIP URI of
                //user to invite to conversation. This class field is referenced
                //by several methods in this class. 
                //***********************************************************
                _targetUri = entry_TextBox.Text;

                RemoteSIP_Label.Content = _targetUri;

                //***********************************************************
                //Call helper method that gets a Contact instance from the supplied
                //target URI and then adds a new conversation.
                //***********************************************************
                StartConversation();
            }
        }

Message_TextBox Events

The following example sends a typing status message to other conversation participants when the user types a character in the Message_TextBox text box. The status of the local user appears for 5 seconds on remote clients every time that InstantMessageModality.BeginSetComposing is called.

        /// <summary>
        /// Raised when user types a character in the IM text box.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Message_TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            //**********************************************************
            //Proceed if conversation exists and is active.
            //**********************************************************
            if (_Conversation != null && _Conversation.State == ConversationState.Active)
            {

                //**********************************************************
                //Proceed if conversation allows typing status to be set.
                //**********************************************************
                if (_Conversation.Modalities[ModalityTypes.InstantMessage].CanInvoke(ModalityAction.SetIsTyping))
                {
                    try
                    {
                        //**********************************************************
                        //Set typing status.
                        //**********************************************************
                        ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSetComposing(true, ComposingCallback, null);
                    }
                    catch (OperationException oe)
                    {
                        MessageBox.Show("Cannot set composing status. Operation Exception " + oe.Message);
                    }
                    catch (NotSignedInException) { };
                }
            }

        }

SpellCheck_Button Event

After the local conversation participant has finished typing new message text to be sent to the other participants, the following example shows how to add the Microsoft Bing spelling checker feature to the application. After the corrections are added, the text is formatted and then sent.

A service reference to the Bing spelling checker is required. For information about how to create the reference, see Getting Started with Bing API, Version 2. The supporting spelling checker code that is added to the application appears in Building Lync IM Conversation Windows: Helper Classes (Part 5 of 5).

        /// <summary>
        /// Handles the event that is raised after spelling checker button is clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SpellCheck_Click(object sender, RoutedEventArgs e)
        {
            this.Cursor = Cursors.WaitCursor;
            SpellCheckStatus_String.Text = "Checking...";
            HttpWebRequest request = BingSpellCheker.BuildSpellRequest(Message_TextBox.Text);

            try
            {
                //***************************************
                // Send the request, and then display the response.
                //***************************************
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                string responseString = BingSpellChecker.GetSpellResponse(response);
                if (responseString.Length > 0)
                {
                    Message_TextBox.Text = responseString;
                    SpellCheckStatus_String.Text = "Bing spelling check is complete";
                }
                else
                {
                    SpellCheckStatus_String.Text = "Bing spelling check is complete. No suggestions found";
                }
            }
            catch (WebException ex)
            {
                //***************************************
                // An exception occurred while accessing the network.
                //***************************************
                Console.WriteLine(ex.Message);
            }
            this.Cursor = Cursors.Default;

        }

SignIn_Button_Click Event

The following example handles the click event for the Sign In button. The button is enabled after Lync 2010 is initialized and the user is not signed in to Lync 2010. For more information about the MSDNArticleIM.ConversationWindow.SignUserIn() method, see Building Lync IM Conversation Windows: Helper Methods (Part 3 of 5).

        /// <summary>
        /// Handles the event that is raised when a user clicks the Sign In button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SignIn_Button_Click(object sender, RoutedEventArgs e)
        {//
            //**************************************
            //This private helper method signs a user into Lync 2010.
            //**************************************
            SignUserIn();      
        }

Send_Button_Click Event

Messages can only be sent on an active conversation with remote participants who are available. A loss of network connectivity during a conversation causes all remote participant availability to be lost but the conversation remains active. The state of the instant messaging modality is suspended while a network is not connected. When network connectivity is restored, remote participant availability is obtained and if the participant is available, messages can be sent.

The following example sends an IM message to a remote user. The example wraps the text obtained from a TextBox control in HTML tags. An instance of IDictionary is declared and then instantiated to hold the selected HTML format enumerator together with the HTML string that is sent to participants. The dictionary is passed into the InstantMessageModalityBeginSendMessage(string, AsyncCallback, object) method as the first argument.

Important

Various HTML formatting attributes are not recognized by Lync 2010. For example, if border attributes for the style tag are set in the example, then the HTML message appears in the Lync 2010 conversation window without border styling.

Tip

If an IM text message is sent in plain-text format, call the overloaded InstantMessageModalityBeginSendMessage(string, AsyncCallback, object) method that takes the message text as a string in the first argument. In this case, do not format the IM text before BeginSendMessage is called.

The first overload of BeginSendMessage can be used to send plain text if you specify Microsoft.Lync.Model.Conversation.InstantMessageContentType.PlainText. However, if the first sent message starts a new conversation and is formatted as plain text, use the second overload of BeginSendMessage that appears in the following example. The first overload of BeginSendMessage can be used for successive messages. Sending a message in an inactive conversation causes the conversation to become active.

Tip

The ConversationMessageReceived() event is raised on the conversation Microsoft.Lync.Model.Conversation.InstantMessageModality whether the message was sent or received by the local user.

        /// <summary>
        /// Handles the event that is raised after a user clicks the Send Message button and sends an IM to all
        /// remote conversation participants
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Send_Button_Click(object sender, RoutedEventArgs e)
        {
            //**********************************************
            //Return this value if a conversation does not exist.
            //**********************************************
            if (_Conversation == null)
            {
                return;
            }

            //***********************************************************
            //Return this value if user has not entered any text in the message box.
            //***********************************************************
            if (Message_TextBox.Text.Length == 0)
            {
                return;
            }
            try
            {
                string messageString = Message_TextBox.Text;
                //*************************************************************
                //Wrap user's message text in an HTML division tag with font attributes
                //taken from text box.  
                //*************************************************************
                string FormattedMessage = "<DIV style=\"font-size:" 
                    + Message_TextBox.FontSize 
                    + "pt;font-family:" 
                    + Message_TextBox.FontFamily.ToString()
                    + ";color: #463939;direction: ltr\">" 
                    + messageString 
                    + "</DIV>";

                //*************************************************************
                //Add html content type key and HTML string to text message dictionary.
                //*************************************************************
                IDictionary<InstantMessageContentType, string> messageDictionary = new Dictionary<InstantMessageContentType, string>();
                messageDictionary.Add(InstantMessageContentType.Html, FormattedMessage);

                //*************************************************************
                //Send the IM message on the conversation IMModality. Call CanInvoke before the message is sent. A remote user can
                //go offline or into do-not-disturb status at any time during a conversation. If this kind of message is sent, an exception
                //is raised by EndSendMessage.
                //
                //Note: If network connectivity is lost on a computer, conversations remain active
                //      but you cannot call BeginSendInstantMessage. When connectivity is restored,
                //      you can begin sending messages on the active conversation again.
                //*************************************************************
                if (
                    ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).CanInvoke(ModalityAction.SendInstantMessage)
                )
                {
                    //*************************************************************
                    //Send the text message by using the message dictionary that is created previously.
                    //*************************************************************
                    ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).BeginSendMessage(
                        messageDictionary
                        , SendMessageCallback
                        , ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]));
                }
                else
                {
                    MessageBox.Show("Cannot send IM now " +
                        ((InstantMessageModality)_Conversation.Modalities[ModalityTypes.InstantMessage]).State.ToString(),
                        "Message Was Not Sent");
                }

            }
            catch (LyncClientException ex)
            {
                MessageBox.Show("Lync Exception: " + ex.Message, "Message Was Not Sent");
            }
            catch (UnauthorizedAccessException)
            {
                MessageBox.Show("Contact cannot accept instant messages");
            }

        }

End_Conversation Button Event

The following example handles the event that is raised after a user clicks the End Conversation button in the conversation Form.

        /// <summary>
        /// Handles the event that is raised after a user clicks the End Conversation button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EndConversation_Button_Click(object sender, EventArgs e)
        {
            //**********************************************
            //Recommendation: Conversation value should not 
            // be null here.
            //**********************************************
            if (_Conversation != null)
            {

                //**********************************************
                //End() cannot be called on a terminated conversation. 
                //**********************************************
                if (_Conversation.State != ConversationState.Terminated)
                {
                    //**********************************************
                    //This terminates the local instance of the conversation.
                    //A remote user is responsible for terminating the local
                    //instance at the remote endpoint.
                    //**********************************************
                    _Conversation.End();
                }
            }
        }

Close_Button_Click Event

The following example closes the conversation window after the user clicks the Close button.

        /// <summary>
        /// Handles the event that is raised after a user clicks the Close button in the conversation window
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Close_Button_Click(object sender, RoutedEventArgs e)
        {
            //Close the Window
            this.Close();
        }

Conclusion

Part 2 discusses the code that runs behind the conversation window controls. Part 3 through part 5 discuss additional coding tasks that are used to build the IM conversation window. In part 2, the events on the MSDNArticleIM.ClientModel helper class and the Lync 2010 API classes are registered. The code appearing in part 2 is used to send IM messages. To receive IM messages and react to changes in the state of the Lync client, the conversation, conversation participants, and the instant message modality, a series of Lync 2010 API events appearing in Building Lync IM Conversation Windows: Lync 2010 API Event Handlers (Part 4 of 5) must be handled. For more information about the helper MSDNArticleIM.ClientModel class, see Building Lync IM Conversation Windows: Helper Classes (Part 5 of 5).

The following Form and control events are discussed in part 2.

  • The Window.Initialized event sets the initial state of the window and then registers for various events.

  • The Window.Closed event unregisters for all registered events and conditionally signs a user out, and then ends the Lync 2010 session with a call to BeginShutdown.

  • The TextBox.LostKeyboardFocus event starts a new conversation.

  • The TextBox.TextChanged event sends a new typing status message to all conversation participants.

  • The Button.Clicked spelling checker event builds and then sends a spelling checker request. The response string is used to update the .Text property of the message text box.

  • The Button.ClickedSign In button event signs a user in to Lync 2010 if the user is not signed in to Lync 2010.

  • The Button.Clicked event on the Send Message button sends the contents of the message box to other conversation participants in an IM format.

  • The Button.Clicked event on the End Conversation button ends the active conversation.

Additional Resources

For more information, see the following resources:

About the Author

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.