Exploring Lync SDK ProposalTracker Sample: Main Page (Part 1 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 first in a three-part series of articles about how to use the ProposalTracker sample Silverlight application.

This article is a tour of the ProposalTracker application that begins with a view of the running application and continues with the components that form the main page. Finally, the tour examines the object model that declares the components and the code that enables the Microsoft Lync 2010 controls on the main page. Part 2 of this series continues the tour with a review of the proposal details page.

Part 3 reviews the Mini ProposalTracker, which is a Conversation Window Extension (CWE) component of ProposalTracker. Mini ProposalTracker demonstrates Microsoft Lync 2010 API contextual conversations. The article guides you through the building of Mini ProposalTracker. You can use the simple code in the sample as a starting point for your own Microsoft Silverlight-based application that uses the Lync 2010 controls.

The ProposalTracker sample application shows how to add seven Lync controls to your Silverlight application that are used to implement Lync communication and presence features. The seven controls are added to the ProposalTracker Silverlight pages by using drag-and-drop operations and include less than twenty lines of Lync-specific C# code. Several XAML element attributes are set to establish the initial state of the controls. That is all that is required to configure the application.

You should be familiar with developing Silverlight applications. Although the article describes how the sample is constructed, it does not explain how to populate Silverlight components such as grids, child controls, or content controls. For more information about Silverlight development, see Silverlight Development Center.

ProposalTracker is a Silverlight web browser application that requires Lync 2010 to be installed on a browser’s host computer. A user must be signed in to Lync 2010 to enable the Lync controls that are used in the sample. This section shows ProposalTracker running in Microsoft Internet Explorer 9 and then shows you where the Silverlight components and Lync controls are assembled at design time.

To compile the sample described in this article, the following software is required.

The ProposalTracker main page presents a business intelligence dashboard to the sales manager at Fabrikam. The dashboard shows the contact list of the signed in user, a ranking of salespeople by performance, a list of active proposals, and a set of address book links.

The sales manager, John Austin1 has signed in to Lync 2010 and can update his current activity and availability using the MyStatusArea Control in the page frame header. The Proposal Team grid contains a ContactList Control that displays the user’s contact list.

A ContactSearchInputBox Control on the main form lets a user search for contacts by name or skill. The results of such a search are returned in the ContactSearchResultList Control that is collapsed under the ContactList.

The Sales Leader Board grid contains a list that is composed of individual PresenceIndicator Control instances that represent the top salespeople on the team. The sales manager can review the details of active proposals that his or her team is pursuing by selecting a proposal from the list in the Active Proposals/In-Play grid.

Figure 1 shows the main ProposalTracker page.

Figure 1. The main ProposalTracker page

The main ProposalTracker page

The details of an individual proposal are shown on the Proposal Details page. This page is opened by selecting an active proposal and then clicking the More link below the proposal summary paragraph. The details page contains a CustomContactList Control representing the members of the proposal team and the ContactCard Control of the proposal sponsor.

Figure 2 shows the Proposal Details page.

Figure 2. The Proposal Details page

The Proposal Details page

The Mini ProposalTracker Conversation Window Extension (CWE) application runs in the CWE of a user who joins a contextual conversation with a ProposalTracker user. Mini ProposalTracker presents one of three kinds of project team size charts. Any conversation participant can change the Team Size chart type and the change is reflected in the CWE for all conversation participants. Figure 3 shows the Mini ProposalTracker application.

Figure 3. Mini ProposalTracker in a CWE

Mini Proposal Tracker in CWE

For more information about Mini ProposalTracker, see Exploring Lync SDK ProposalTracker Sample: MiniProposalTracker CWE Application (Part 3 of 3).

ProposalTracker consists of two Silverlight pages. Each page contains some combination of PageFrame, Grid, and custom controls.

MainPage page uses grids and frames to arrange a set of CustomContent controls such as the ActiveProposalControl, ProposalTeamControl, SalesLeaderboardControl, and the AddressBookLinksControl. The Lync controls are identified by the green elements in figure 4 and are contained in these custom controls except for the MyStatusArea Control.

Figure 4. The layout of the main page

The layout of the main page

In the kind of production application that you write using the Lync controls, a data source such as Active Directory Domain Services or an SQL database is used to provide SIP addresses to the controls that have a Source property. ProposalTracker is a sample that merely shows how the controls work. Consequently, it uses two small classes to provide a set of SIP address to these controls instead of connecting to a data source. These classes expose embedded constant strings as SIP addresses. The SIP addresses are wired to the Lync controls by using a combination of declarative XAML and code-behind logic. The logic that makes ProposalTracker work is in the following functional areas:

  • Code only classes that encapsulate salespersons and proposals.

  • A code only class that encapsulates a sales team.

  • Code-behind partial classes that bind the embedded constant SIP addresses to Lync controls.

  • Code only classes that enable ProposalTracker to show or collapse the ContactSearchResultList Control.

Main page logic consists of the code-behind logic for the ActiveProposalsControl, SalesLeaderBoardControl, and ProposalTeamControl. The C# logic behind these controls sets item source properties for list boxes and toggles page elements between visible and collapsed states. The appearance of all controls is set using declarative XAML.

The following example declares the “controls” namespace in the document element of each XAML page that uses Lync controls.

xmlns:controls="clr-namespace:Microsoft.Lync.Controls;assembly=Microsoft.Lync.Controls"

ProposalTracker Object Model

ProposalTracker is implemented by the classes that appear in the following tables. The tables provide the name of each class together with the purpose of the class. The third column shows which Lync controls are used in a class.

The main page of ProposalTracker includes the following components.

Class name

Description

Lync feature helper

MainPage

Defines the main page of ProposalTracker by using a set of PortletFrame user controls.

MyStatusArea Control.

ActiveProposalsControl

PortletFrame contents. Defines a list box and text area to display active proposals.

None.

AddressBookLinksControl

PortletFrame contents. Defines a set of buttons in a grid.

None.

ProposalTeamControl

PortletFrame contents. Contains a list of all proposal team members.

ContactSearchInputBox Control, ContactSearchResultList Control, and ContactList Control.

SalesLeaderBoardControl

PortletFrame contents. Defines list box of salespeople ranked by sales.

PresenceIndicator Control. The value of the Source property of each instance of this control is provided by the SalesPerson.SalesPersonUri property.

The proposal details page of ProposalTracker includes the following components.

Class name

Description

Lync feature helper

ProposalDetails

Defines a page that shows the details of a proposal.

ContactCard Control and CustomContactList Control. The values of the ContactCard.Source, CustomContactList.ItemsSource, and CustomContactList.ContextualInformation property are provided by an instance of Proposal.

TeamSizeControl

Defines a ViewBox image that shows the complexity of a proposal.

None.

ComplexityControl

Defines a ViewBox image that shows the size of a proposal team.

None.

The classes in the following table define the general appearance and helper code of ProposalTracker.

Class name

Description

Lync feature helper

PageFrame

Defines the visual elements of the main page and proposal details page.

None.

PortletFrame

Defines a grid that contains an individual user control. Frame icon and user control content are properties of a PortletFrame. A PortletFrame is used in the main page and proposal details page.

None.

BooleanToVisibility

Implements IValueConverter and takes a Boolean value and converts to Visibility.Visible or Visibility.Collapsed. Used to show or hide the ContactSearchResultList Control.

None.

NotBooleanToVisibility

Overrides BooleanToVisibilty and returns the inverse equivalent of Boolean argument as Visibility. Used to show or hide the ContactList Control.

None.

SalesTeam

Creates a collection of SalesPerson instances and a collection of Proposal instances. The collections are public and consumed by other classes.

The SalesPersonListBox.ItemsSource property is set to SalesTeam.SalesPeople.

SalesPerson

Encapsulates the details of a sales person.

The SalesPerson.SalesPersonUri property value is used to set the value of the ContactBase.Source property of contact-specific controls.

Proposal

Encapsulates the details of a proposal.

Sponsor SIP Uri, proposal team member SIP Uri strings and contextual conversation information.

The ProposalTracker solution references four Lync 2010 API assemblies at design time. They include the following:

  • Microsoft.Lync.Controls

  • Microsoft.Lync.Controls.Framework

  • Microsoft.Lync.Model

  • Microsoft.Lync.Utilites

These four assemblies are referenced from the %ProgramFiles%\Microsoft Lync\SDK\Assemblies\Silverlight folder.

In addition to the Lync 2010 API assemblies, the ProposalTracker solution references other Microsoft assemblies that are added to support Silverlight 4 development.

XAML Declarations

The XAML declarations in this section declare the Lync controls on the main page.

MyStatusArea Control

The MyStatusArea control displays the status of the user who is signed in to Lync 2010. At run time, this control shows the current status and activity message of the signed in user. You do not have to set any properties for the control.

Figure 5. MyStatusArea control

The MyStatusArea control

The following example declares the MyStatusArea control and sets its background to transparent so that the color of the page header shows through the white space of the control.

            <localControls:PageFrame.PageStatusArea>
                <controls:MyStatusArea Background="Transparent"/>
            </localControls:PageFrame.PageStatusArea>

ContactList

The ContactList Control encapsulates the features of the contact list in the Lync 2010 client. The contact list of the signed-in user is displayed without setting a source property. This control is typically visible and the ContactSearchResultList Control is hidden behind it. ProposalTracker makes it appear that ContactList Control shows search results. However, ProposalTracker is hiding the contact list and showing the contact list search results in its place. Figure 6 shows the ProposalTracker ContactList Control in the area immediately below where the ContactSearchInputBox is displayed.

Figure 6. ContactList control

The ContactList control

The following example declares ContactList Control and binds the Visibility property to the NotBooleanToVisibility converter that is referenced from the ProposalTracker assembly.

            <!--ContactList Control-->
            <!--The ContactList Control will only be available if the search input box is cleared-->
            
            <controls:ContactList Background="White"
                Visibility="{Binding
                                    Path=SearchState,
                                    ElementName=ocSearchInput,
                                    Converter={StaticResource NotBooleanToVisibility},
                                    ConverterParameter=cleared}"
                Margin="0,40,0,0"
                Grid.RowSpan="2"
                />


ContactSearchInputBox

The ContactSearchInputBox Control lets a user search their organization for other users or groups based on name, telephone number, or skill. The results of such a search are presented in the ContactSearchResultList Control. Figure 7 shows the ContactSearchInputBox and the ContactList as they appear in ProposalTracker.

Figure 7. The ContactSearchInputBox and ContactList

The ContactSearchInputBox and ContactList

The following example declares a ContactSearchInputBox Control.

            <!--ContactSearchInputBox allows the user to type in the search parameter-->
            <!--The ContactsearchResultlist is bound to this control to show the results returned-->
            <controls:ContactSearchInputBox
                x:Name="ocSearchInput"
                MaxResults="10"
                Grid.Row="0"
                />

ContactSearchResultList

By default, ProposalTracker hides the ContactSearchResultList Control. It is shown instead of the ContactList Control when Lync 2010 returns the result of a contact search. This control is bound to the ContactSearchInputBox Control. Figure 8 shows the ContactSearchResultList control displaying the results of a partial name search.

Figure 8. The ContactSearchResultList with the results of a partial name search

The ContactSearchResultList by name

Figure 9 shows the results of a search for contacts or groups by name. In this search, a set of distribution groups is returned but individual contacts may also be returned.

Figure 9. The ContactSearchResultList with the results of a skill search

The ContactSearchResultList by skill

TipTip

If you do not have to display the results of a contact search in a separate location on a page or collapse the results to display a contact list in their location, use the ContactSearch Control. By using this control, the search input box and results list are combined as one control. The results appear immediately below the search input.

The following example declares a ContactSearchResultList Control.

            <!--ContactSearchResultList control will be available when a character is typed into the contact search input box above-->
            <!--If there is any return, it will show up in the list. If there is no return a not found notice will appear.-->
            <!--The Contact List will be displayed if the content of the SearchInputBox is cleared-->

            <controls:ContactSearchResultList
                x:Name="ocSearchResults"
                Grid.Row="1"
                ItemsSource="{Binding Results, ElementName=ocSearchInput}"
                Visibility="{Binding
                                    Path=SearchState,
                                    ElementName=ocSearchInput,
                                    Converter={StaticResource BooleanToVisibility},
                                    ConverterParameter=cleared}" Grid.RowSpan="2" />


PresenceIndicator

The PresenceIndicator Control is a stand-alone control that displays the presence of the contact specified by the PresenceIndicator.Source attribute. The data source of the Sales Leader Board list that contains the collection of PresenceIndicator is an ObservableCollection of SalesPerson instances. Figure 10 shows the salesperson leaderboard list populated with a collection of presence indicators whose source attribute values are SalesPerson.SalesPersonUri.

Figure 10. SalesPersonLeaderBoard control

SalesPersonLeaderBoard. List of PresenceIndicator

The following example declares a PresenceIndicator Control. The ContactBase.Source property of the control is bound to a contact SIP address exposed as SalesPerson.SalesPersonUri.

                            <controls:PresenceIndicator
                                x:Name="presenceIndicatorControl"
                                Source="{Binding Path=SalesPersonUri}"
                                Grid.Column="0"
                                VerticalAlignment="Center"
                                />

SalesPerson and Proposal Collections

The SalesTeam class has static methods to create the salesperson and proposal collections. Instances of the SalesPerson and Proposal classes are added to the collections. The first reference to SalesTeam is made in the ActiveProposalsControl constructor. The static SalesTeam constructor loads the proposal list and salesperson list. Each Proposal in the collection has the proposal details and application data that is needed to start a contextual IM conversation. The SalesPeople collection is the data source for the SalesPersonsListBox control in the SalesLeaderBoardControl. It provides the SIP URI that is used as the ContactBase.Source property of the PresenceIndicator Control instances that form the sales leaderboard list.

SalesTeam Class

The following example is a partial listing of the static SalesTeam class. The example declares the salesperson and proposal collections that are used to populate the salesperson and active proposals list boxes.

    /// <summary>
    /// Class that models a sales team
    /// </summary>
    public static class SalesTeam
    {
        public static ObservableCollection<SalesPerson> SalesPeople { get; set; }
        public static ObservableCollection<Proposal> Proposals { get; set; }
...
    }

The following example is the SalesTeam static constructor. The constructor is called implicitly in the first reference to the SalesTeam class.

        static SalesTeam()
        {
            SalesPeople = new ObservableCollection<SalesPerson>();
            Proposals = new ObservableCollection<Proposal>();
            LoadProposals();
            LoadSalesPeople();
        }

The following example creates instances of the SalesPerson class. The SalesPerson instances are added to the SalesPeople collection.

        /// <summary>
        /// Creates instances of SalesPerson and loads instance into public static ObservableCollection. 
        /// The ObservableCollection is the data source of the SalesPersonLeaderBoard ListBox.
        /// </summary>
        public static void LoadSalesPeople()
        {
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:bob@fabrikam.com", TotalSales = 200 });
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:mary@fabrikam.com", TotalSales = 159 });
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:martin@fabrikam.com", TotalSales = 137 });
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:kathy@fabrikam.com", TotalSales = 146 });
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:john@fabrikam.com", TotalSales = 109 });
            SalesPeople.Add(new SalesPerson { SalesPersonUri = "sip:sam@fabrikam.com", TotalSales = 112 });
        }

The following example creates instances of Proposal and then adds the instance to the Proposals collection.

        /// <summary>
        /// Creates instances of Proposal and adds new instance to Proposals collection. 
        /// Proposals collection is the data source of the active proposals list box of ActiveProposalsControl.xaml
        /// </summary>
        public static void LoadProposals()
        {
            Proposals.Add(new Proposal
                              {
                ProjectName = "City Power and Light",
                ClosedDate = "08/01/2010",
                Description = "Discovery project with full multi-platform Widget implementation for global divisions. Estimated TTC is 9-12 months. Projected Fabrikam revenue of $1.2M. Strong client relationship, likely close.",
                SponsorUri = "sip:bob@fabrikam.com",
                Team = new List<String> { "sip:mary@fabrikam.com", "sip:kathy@fabrikam.com", "sip:martin@fabrikam.com", "sip:john@fabrikam.com", "sip:peter@fabrikam.com" }
            });
            Proposals.Add(new Proposal
                              {
                ProjectName = "Datum Corporation",
                ClosedDate = "09/20/2010",
                Description = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla a interdum ligula. Quisque velit magna, rhoncus non commodo at, ultricies sed neque. Nulla quis orci quam. Phasellus nec lacus erat.",
                SponsorUri = "sip:mary@fabrikam.com",
                Team = new List<String> { "sip:bob@fabrikam.com", "sip:kathy@fabrikam.com", "sip:martin@fabrikam.com", "sip:john@fabrikam.com", "sip:peter@fabrikam.com" }
            });
...
        }

The embedded constant strings in the Proposal.Team property are the SIP address source for the CustomContactList Control that is used on the proposal details page that is discussed in Exploring Lync SDK ProposalTracker Sample: Proposal Details (Part 2 of 3).

SalesPerson Class

The SalesPerson class exposes a string property that holds the SIP address of a salesperson and a numeric property that exposes total sales. In the following example, the SalesPersonUri property value is bound to the Source property of a PresenceIndicator Control.

using System;

namespace ProposalTracker.Models
{
    /// <summary>
    /// Class that models a Sales Person
    /// </summary>
    public class SalesPerson
    {
        public String SalesPersonUri { get; set; }
        public Double TotalSales { get; set; }
    }
}


Proposal Class

The following example is the Proposal class. This simple class exposes several properties that are the source of proposal details on both the main page and proposal details pages. Three of these properties are used by Lync controls.

  • Team: The source of SIP addresses that define the contents of the CustomContactList Control on the Proposal Details page.

  • SponsorUri: The source of the ContactCard Control of the project sponsor on the Proposal Details page.

  • ContextualInfo: The application identifiers that point to a registered contextual application that is used by ProposalTracker and Mini ProposalTracker.

using System;
using System.Collections.Generic;
using Microsoft.Lync.Controls;

namespace ProposalTracker.Models
{
    /// <summary>
    /// Class that models a proposal
    /// </summary>
    public class Proposal
    {
        private ConversationContextualInfo _conversationContextualInfo;
        public String ProjectName { get; set; }
        public String Description { get; set; }
        public String ClosedDate { get; set; }
        public List<String> Team { get; set; }
        public String SponsorUri { get; set; }

        //Field to create and return a ContextualInfo of a proposal.
        //This is used in the MiniProposalProject to initiate a Contextual Conversation.
        public ConversationContextualInfo ContextualInfo { 
            get
            {
                //If _conversationContextualInfo is null, create a new one and return it.
                //If not return itself. 
                return _conversationContextualInfo ??
                       (_conversationContextualInfo = new ConversationContextualInfo{
                                                       //Description is taken from the proposal details
                                                       ApplicationData = Description, 
                                                       //This should be the same GUID you're using in the reg file.
                                                       ApplicationId = "{AFCFD912-E1B7-4CB4-92EE-174D5E7A35DD}",
                                                       //Project name is the current proposal's projectname.
                                                       Subject = ProjectName,
                                                       //This is a link to the main page. It is optional but good to have.
                                                       ContextualLink = "file:\\\\fabrikam_server\\Users\\Public\\MiniProposalTracker\\bin\\debug\\MiniProposalTrackerTestPage.html"
                                                      }
                       );
            }
        
        }
    }
}


Code-Behind Logic

The following example is from ActiveProposalsControl.ActiveProposalsControl(). The example binds the proposal collection to the proposals list. The Proposal Details page uses the SIP addresses of proposal team member collection to define the members of the CustomContactList Control that is displayed in the Proposal Team portlet frame.

//Bound the ListBox to a list of Proposals. The static class SalesTeam provides the list of proposals.
 proposalsListBox.ItemsSource = SalesTeam.Proposals;

The following example is from the SalesLeaderBoardControl constructor. It sets the salesperson list data context to the SalesPeople collection in the previous example. Setting the data context lets you bind to items in the collection and public properties of individual items.

//Bind the listbox to a list of Salespeople. List is retrieved from the static class Sales Team.
SalesPersonsListbox.ItemsSource = SalesTeam.SalesPeople;

In addition to setting the data context for the list, ProposalTracker binds the ContactBase.Source property of individual PresenceIndicator Control instances to the SalesPerson.SalesPersonUri property.

Show and Hide Contact Search Results

ProposalTracker sets the Visibility attribute of the ContactSearchResultList Control and ContactList Control by binding to two ProposalTracker classes that implement IValueConverter. The Converter methods in these classes return enumerators of the Silverlight System.Windows.Visibility enumeration. If the SearchState of the ContactSearchInputBox Control is Cleared, then the Visibility of the ContactList Control is set to Visibility.Visible and the ContactSearchResultList Control Visibility is set to Visibility.Collapsed.

When a user enters characters in the text box of the ContactSearchInputBox Control and then presses the Enter key, the value of SearchState is no longer Cleared. The ContactList Control is collapsed and the ContactSearchResultList Control is now visible.

For information about how to use IValueConverter, see Delay's Blog.

The following example declares the two Lync 2010 contact list related controls, binds the Visibility attributes to converters, and sets the Visibility attribute values to the results of a conversion.

The following ContactSearchInputBox properties are set.

  • X:Name: The unique name of the control. The Visibility property of the ContactList and ContactSearchResultList refer to the search input box by setting ElementName to this value.

  • MaxResults: The maximum number of results to return from a search.

    TipTip

    Setting this property to a large number such as 100 can result in slow search performance.

            <!--ContactSearchInputBox allows the user to type in the search parameter-->
            <!--The ContactsearchResultlist is bound to this control to show the results returned-->

            <controls:ContactSearchInputBox
                x:Name="ocSearchInput"
                MaxResults="10"
                Grid.Row="0"
                />

            <!--ContactSearchResultList control will be available when a character is typed into the contact search input box above-->
            <!--If there is any return, it will show up in the list. If there is no return a not found notice will appear.-->
            <!--The Contact List will be displayed if the content of the SearchInputBox is cleared-->

            <controls:ContactSearchResultList
                x:Name="ocSearchResults"
                Grid.Row="1"
                ItemsSource="{Binding Results, ElementName=ocSearchInput}"
                Visibility="{Binding
                                    Path=SearchState,
                                    ElementName=ocSearchInput,
                                    Converter={StaticResource BooleanToVisibility},
                                    ConverterParameter=cleared}"
                />

            <!--ContactList Control-->
            <!--The ContactList Control will only be available if the search input box is cleared-->
            <controls:ContactList
                Grid.Row="0"
                Background="White"
                Visibility="{Binding
                                    Path=SearchState,
                                    ElementName=ocSearchInput,
                                    Converter={StaticResource NotBooleanToVisibility},
                                    ConverterParameter=cleared}"
                Margin="0,40,0,0"
                Grid.RowSpan="2"
                />


When you drop the ContactSearchResultList Control onto the page, set the ItemsSource and Visibility properties. By default, these properties are empty.

  • ItemsSource: A data-bound property that binds the result of a contact search to the control at runtime.

  • Visibility: A data-bound property that binds to a method that returns a System.Windows.Visibility enumeration. The enumeration is set to either Visibility.Visible or Visibility.Collapsed.

Part 1 shows how the components of the main page of ProposalTracker are arranged, where the Lync controls are used, and how the controls are connected to the page. Exploring Lync SDK ProposalTracker Sample: Proposal Details (Part 2 of 3) describes how the Proposal Details page is created and how it lets a user start contextual conversations with Mini ProposalTracker.

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: