Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Creating an Office Communicator Application for Do Not Disturb

Pete Brown - 17 October 2010

When meeting with other people at Microsoft, my computer is also my phone. I rely on Office Communicator 2007 R2 more than many other applications.

Family members wandering into my home office can't really tell I'm on a call unless I happen to be talking right then. I normally have my headphones on anyway, and I use my Samson mic for voice. Usually my wife stands out of camera range and mimes "Are you on the phone?". My kids just wander in regardless, often half-dressed, which makes for some pretty funny meetings ;)

So, I thought it would be neat to create a little application that could show "On-Air" or similar when parked on a small monitor (think a USB MIMO display) facing towards the door. When I build my new office in the next room, I'll actually stick this outside the door so people can see before wandering in.

I haven't purchased the MIMO yet, as I wanted to do a little proof-of-concept project first. The app described in this post is that proof-of-concept.

Project Setup

Start up a new WPF application. I named mine "PeteBrown.CommunicatorDisplay". Next, add a reference to the communicator API.

To integrate with communicator, you'll want to download the Microsoft Office Communicator 2007 SDK. Once installed, you can browse to the DLLs inside the Add Reference dialog for your project.

image

CommunicatorAPI is the standard API which can be used to interact with communicator, showing dialogs etc. CommunicatorPrivate provides much of the same functionality, but without displaying any UI. Both libraries were installed to c:\Program Files(x86)\Microsoft Office Communicator\SDK

Working with the API - the CommunicatorService Class

For this little app, I'll need to subscribe to an event that will tell me when my status changes. The event, in this case is OnMyStatusChange. To keep the code somewhat cleaner, I decided to package up all the Communicator interaction (of which there is little in this app), including the wiring of OnMyStatusChange, into a single CommunicatorService class.

public enum CommunicatorStatus
{
    Available,
    OnPhone
}

public class CommunicatorService : IDisposable
{
    private Messenger _messenger;

    public CommunicatorService()
    {
        _messenger = new Messenger();
        _messenger.OnMyStatusChange += new DMessengerEvents_OnMyStatusChangeEventHandler(OnMyStatusChange);

        //TODO: Map startup status
    }

    private void OnMyStatusChange(int hr, MISTATUS mMyStatus)
    {
        System.Diagnostics.Debug.WriteLine(mMyStatus.ToString());
        switch (mMyStatus)
        {
            case MISTATUS.MISTATUS_IN_A_CONFERENCE:
            case MISTATUS.MISTATUS_IN_A_MEETING:
            case MISTATUS.MISTATUS_ON_THE_PHONE:
                Status = CommunicatorStatus.OnPhone;
                break;

            case MISTATUS.MISTATUS_ONLINE: 
            case MISTATUS.MISTATUS_OFFLINE:
            case MISTATUS.MISTATUS_AWAY:
                Status = CommunicatorStatus.Available;
                break;

        }
    }

    public event EventHandler StatusChanged;
    private CommunicatorStatus _status;
    public CommunicatorStatus Status
    {
        get { return _status; }
        set
        {
            if (_status != value)
            {
                _status = value;

                if (StatusChanged != null)
                    StatusChanged(this, EventArgs.Empty);
            }
        }
    }

    public void Dispose()
    {
        if (_messenger != null)
        {
            _messenger.OnMyStatusChange -= OnMyStatusChange;
            _messenger = null;
        }
    }

    ~CommunicatorService()
    {
        if (_messenger != null)
            Dispose();
    }
}

Note that I implement IDisposable here. I wanted to clean up the Communicator hooks. It's very likely overkill, but if I end up doing more with this class in the future, I'll be class it's in place.

Once I had the service set up, I create a view model class to act as the glue between the view and the service.

The ViewModel

This application has only a single viewmodel class. That's due to having only a single view. This class is responsible for creating the communicator service instance, and for handling the StatusChanged event. It then takes the information from the service and maps it to a set of bindable properties used by the UI.

public class MainViewModel : Observable
{
    private CommunicatorService _communicatorService;

    public MainViewModel()
    {
        _communicatorService = new CommunicatorService();
        _communicatorService.StatusChanged += new EventHandler(OnCommunicatorStatusChanged);
    }

    void OnCommunicatorStatusChanged(object sender, EventArgs e)
    {
        switch (_communicatorService.Status)
        {
            case CommunicatorStatus.Available:
                StatusText = "Available";
                DoNotDisturb = false;
                break;

            case CommunicatorStatus.OnPhone:
                StatusText = "On the Phone";
                DoNotDisturb = true;
                break;
        }
    }

    private string _statusText = string.Empty;
    public string StatusText
    {
        get { return _statusText; }
        set
        {
            if (_statusText != value)
            {
                _statusText = value;
                NotifyPropertyChanged("StatusText");
            }
        }
    }

    private bool _doNotDisturb = false;
    public bool DoNotDisturb
    {
        get { return _doNotDisturb; }
        set
        {
            if (_doNotDisturb != value)
            {
                _doNotDisturb = value;
                NotifyPropertyChanged("DoNotDisturb");
            }
        }
    }
}

As is my usual approach, I created a simple base class named "Observable" to handle the INotifyPropertyChanged cruft.

public abstract class Observable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

The User Interface

The UI is pretty basic. If I'm not to be disturbed, the color is Red. If I'm available, the color is green. In both cases, the status text is displayed in white against this background color.

<Window x:Class="PeteBrown.CommunicatorDisplay.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:System.Windows.Controls;assembly=PresentationFramework"
        Title="MainWindow" Height="400" Width="800">
    
    <Window.Resources>
        <controls:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
    </Window.Resources>
    
    <Grid>
        <Rectangle Fill="DarkGreen" />
        <Rectangle Fill="DarkRed"
                   Visibility="{Binding DoNotDisturb, Converter={StaticResource BooleanToVisibilityConverter}}" />
        <TextBlock Text="{Binding StatusText}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontSize="120"
                   Foreground="White"
                   TextWrapping="Wrap" />
    </Grid>
</Window>

I handled the display of the red background using the built-in BooleanToVisibilityConverter in WPF in a binding statement against the DoNotDisturb property of the viewmodel. The text displayed comes from the StatusText property of the viewmodel. The green background is always there, but it is revealed only when the red background is collapsed.

The Window is sized fro the typical 800x400 resolution of some of the mini USB displays. Some other displays have different resolutions; adjust as necessary.

The code-behind for the window is pretty brief:

public partial class MainWindow : Window
{
    MainViewModel _vm;

    public MainWindow()
    {
        InitializeComponent();

        Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        _vm = new MainViewModel();
        DataContext = _vm;
    }
}

All I do there is instantiate the viewmodel and assign it as the DataContext for the window.

Test Run

To test, I called my mobile phone from communicator. Within a couple seconds, the WPF application picked up and displayed the correct status:

image

Once I hung up, I got the expected "Available" status. Remember, I don't care if the status is "Away" or anything like that; this is just to tell people whether or not it's ok to wander into the office and ask me a question

image

That's all there is to it from a proof-of-concept level of application.

Wrap-Up

The Communicator API is simple to use from a WPF application, even without using some of the wrapper WPF presence controls.

Of course, this is a proof-of-concept. To make this more robust, I'll want to add in code to handle disconnection and shutdown, as well as other events. I'll also need to handle other statuses to ensure I have all the types of meetings covered. I'd want to check the status at startup and make sure I have that handled as well. Finally, this whole application could be refactored to use a custom control with visual states for each of the communicator statuses rather than the DoNotDisturb boolean value.

Oh, and plugging in other apps would be nice. For example, if Camtasia is running, it would be nice to automatically set this to stay "Recording" or something like that.

         

Source Code and Related Media

Download /media/68652/petebrown.communicatordisplay.zip
posted by Pete Brown on Sunday, October 17, 2010
filed under:          

10 comments for “Creating an Office Communicator Application for Do Not Disturb”

  1. Davesays:
    Here is an idea. Expose your status as a service and write a WP7 app for it. Buy you wife a WP7 phone and install this app. Now your wife doesn't even need to walk by your office to see if you are busy. Just a thought. Cool idea.
  2. Gosays:
    _messenger.OnMyStatusChange += new DMessengerEvents_OnMyStatusChangeEventHandler(OnMyStatusChange)
    I am getting error " InvalidCastException was unhandled" while executing above statement.
    Please suggest.
  3. Adriansays:
    Hi,

    Thanks for this post, I'm working on something similar but I encounter a problem and maybe you know why it is happening.

    OnMyStatusChange is only triggered when Outlook is open for some reason. My outlook is integrated with communicator as well, so there is some connection between them. The Form changes only when I have Outlook opened, and it does not work when it is not open, even having the communicator on of course. Do you have any idea what do I need to change to make the program work with or without outlook?

    Thanks for any help on this.
  4. Rupali Rautsays:
    Hello,

    I have added a menu item in right click context on office communicator. when I click on the item by default conversation window gets loaded and then i am able to access the contacts.
    I need your help is there any way to hide or disable the opened instance of conversation window.

    thans

Comment on this Post

Remember me