Monthly Archives: March 2011

Real-time Client-Server Communication on Windows Phone with Long Polling

I was looking for some information on Push technologies and the first thing I found was the Wikipedia article which among other things mentioned a technique called “Long Polling”. This seemed like a really feasible solution to the problem I have seen many people struggling with – getting real-time communication from the phone. Now I can see some limitations – an app needing to keep running to keep it from losing connection (this might change with one of the upcoming updates though if it enables multitasking), possible data usage or server security exposure problems, but I have little experience with server development, so why would I worry about that? Smile

I decided to try writing a Windows Phone 7 app with a back-end WCF service and a non-WP7-Silverlight app to talk to. This is simple – just create a solution in VS and a project for each of these three programs.

image

Let’s start with the WCF service.

Configure the WCF service to run on a specific port – go to project/properties/web and set “Specific port” to 2333. This will give you a reliable location to reference the service.

Add clientaccesspolicy.xml to the WCF service to let the SL4 app connect.

Rename (Refactor/Rename) Service1 to ChatService and define the service contract:

[ServiceContract]
public interface IChatService
{
    [OperationContract]
    void Send(string message, string recipient);
 
    [OperationContract]
    string GetMessage(string recipient);
}

As you can see – the service class will be simplified to only send messages to a given recipient and let a recipient poll for a message. This is the service class definition:

public class ChatService : IChatService
{
    // A list of messages for each recipient
    private static Dictionary<string, List<string>> messages = 
        new Dictionary<string, List<string>>();
 
    private static object syncObject = 
        new object();
 
    private static readonly TimeSpan Timeout = 
        TimeSpan.FromSeconds(50);
    
    public void Send(string message, string recipient)
    {
        lock (syncObject)
        {
            if (!messages.ContainsKey(recipient))
                messages.Add(recipient, new List<string>());
 
            messages[recipient].Add(message);
        }
    }
 
    public string GetMessage(string recipient)
    {
        var start = DateTime.Now;
 
        do
        {
            lock (syncObject)
            {
                if (!messages.ContainsKey(recipient))
                    messages.Add(recipient, new List<string>());
                else
                {
                    if (messages[recipient].Count > 0)
                    {
                        var message = messages[recipient][0];
                        messages[recipient].RemoveAt(0);
                        return message;
                    }
                }
 
                // Yes, just faking it out a bit to avoid the distractions from events,
                // resetevents etc.
                Thread.Sleep(50);
            }
        } while (DateTime.Now - start < Timeout);
 
        return null;
    }
}

We have a dictionary that has a list of messages for each registered recipient, the Send method just adds a message to the list and the GetMessage keeps polling to see if there is a new message in the list, then either returns one if one shows up or times out and returns null. The client will handle nulls and timeouts and interpret them as the good time to start another long poll.

Now do both WP7 and SL4 apps in parallel

Add service reference to both Silverlight projects, referencing http://localhost:2333/ChatService.svc as ChatService.

Now edit MainPage.xaml files for both apps – set x:Name=”root” on the root elements, then add this simplistic view as its Content shared by both apps:

 <Grid
    x:Name="LayoutRoot"
    Loaded="LayoutRoot_Loaded">

    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
 
    <ListBox
        Grid.Row="0"
        Grid.ColumnSpan="2"
        ItemsSource="{Binding Messages, ElementName=root}" />

    <TextBox 
        x:Name="InputBox"
        Grid.Row="1"
        Grid.Column="0"
        Text="{Binding Message, Mode=TwoWay, ElementName=root}" />

    <Button
        x:Name="SendButton"
        Grid.Row="1"
        Grid.Column="1"
        Click="SendButton_Click"
        Content="Send"
        Padding="10,0" />

</Grid>

The code behind for both will be almost identical – just swap recipients. The one below is for the WP7 version – it sends messages to “SLPC” and gets messages for “WP7”.

public partial class MainPage : PhoneApplicationPage
{
    public static readonly DependencyProperty MessagesProperty =
        DependencyProperty.Register("Messages", typeof(ObservableCollection<string>), typeof(MainPage),
            new PropertyMetadata(null));
 
    public ObservableCollection<string> Messages
    {
        get { return (ObservableCollection<string>)GetValue(MessagesProperty); }
        set { SetValue(MessagesProperty, value); }
    }
 
    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(MainPage),
            new PropertyMetadata(""));
 
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }
 
    // Constructor
    public MainPage()
    {
        this.DataContext = this;
        Messages = new ObservableCollection<string>();
        InitializeComponent();
    }
 
    private void SendButton_Click(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.SendAsync(Message, "SLPC");
        Message = "";
    }
 
    private void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
    {
        var svc = new ChatService.ChatServiceClient();
        svc.GetMessageCompleted += OnGetMessageCompleted;
        svc.GetMessageAsync("WP7");
    }
 
    private void OnGetMessageCompleted(object sender, ChatService.GetMessageCompletedEventArgs e)
    {
        var svc = (ChatService.ChatServiceClient) sender;
 
        if (e.Error is TimeoutException ||
            (e.Error == null && e.Result == null))
        {
            svc.GetMessageAsync("WP7");
            return;
        }
 
        if (e.Error != null)
            MessageBox.Show(e.Error.Message);
        else
        {
            Messages.Add(e.Result);
        }
 
        svc.GetMessageAsync("WP7");
    }
}

As you can see we have two dependency properties for the view to bind to – the list of Messages and the Message to send. Clicking on Send will simply send the message through the service proxy, while as soon as the page is loaded – polling on GetMessage starts and OnGetMessageCompleted will check for timeouts and nulls, show any messages incoming and reissue requests to GetMessage.

Conclusion

What we get is this – a Windows Phone and a Silverlight 4 apps talking to one another with virtually no delay.

image

Now there are a lot of simplifications here – no authentication of clients, hardcoded recipients, the server using polling to check for new messages from other threads etc. but it works!

I am sure this can be improved and perhaps there are better or simpler ways to achieve this and I am open to comments.

Advertisements
Tagged , , , , ,

Binding to properties of a custom property in WPF, Silverlight 4 or Windows Phone 7

I was trying to create a UserControl for Windows Phone 7 yesterday and I wanted to enable specifying a  collection-type property that I could configure in my View/XAML file.

I have registered a DependencyProperty, but since I have not done many UserControls or CustomControls in WPF or SL, so I thought I would look how it is done in other similar places – like Grid.ColumnDefinitions. OK, so it is a custom type RowDefinitionCollection based on PresentationFrameworkCollection<RowDefinition>. I thought an ObservableCollection<MyItemType> or a List<> would work, but something did not work right, so I thought derive from PresentationFrameworkCollection<>, but when I tried – it turned out it does not have an accessible parameterless constructor which turned out to be some sort of an issue. I really wanted to make it work, so I thought – OK, I will implement my own version, so I created my collection type as a wrapper around an ObservableCollection, that derives from a FrameworkElement (I played with DependencyObject too) and implements all these interfaces – ICollection, ICollection<>, IList, IList<>, IEnumerable, IEnumerable<>.

All was good – I defined MyItemType as deriving from a FrameworkElement too and added some properties, among others an ICommand-typed-Command and CommandParameter. All hell broke loose when I tried to bind to the Command in XAML.

I kept getting these XamlParseException AG_E_PARSER_BAD_PROPERTY_VALUE on the line that had the binding to the Command. I tried looking at this from all angles of Google and Bing and could not find the right solution, until I tried the below and got the same problem!

<Grid>
    <Slider
        x:Name="slider"
        VerticalAlignment="Top"
        HorizontalAlignment="Stretch"
        Minimum="0"
        Maximum="100" />

    <Grid
        VerticalAlignment="Bottom"
        HorizontalAlignment="Stretch"
        Height="100">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="{Binding Value, ElementName=slider}"/>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
 
        <Rectangle
            Fill="Red"
            Grid.Column="0"
            VerticalAlignment="Stretch"
            HorizontalAlignment="Stretch" />
    </Grid>
</Grid>

OK, so it seems like binding on ColumnDefinitions is not supported on WP7 either. Tried the same snippet in WPF – worked great. Silverlight 4 – no exception, but it just would not update my rectangle. So it seems like Silverlight is limited in that regard.

That was close – I started looking into it more and found that it would almost work, but the DataContext on my collection would not get updated whatever I do. Finally – I found two things I needed to do:

  • Handle DataContext changes on my UserControl and update the DataContext on my collection and do the same from the collection to MyItemType. Only thing – there is no DataContextChanged event. Sheesh… Fortunately we have our MVPs to the rescue – I found a solution on CodeProject quite easily – http://www.codeproject.com/Articles/38559/Silverlight-DataContext-Changed-Event.aspx – just added IDataContextChangedHandler to both the control and the collection and update the DataContext on their properties.
  • Made sure that MyItemsProperty DependencyProperty registration does not specify the default value as “new MyItemsCollection(MyUserControl)”, but rather do it in the constructor – otherwise all instances of the control would share the same collection! This is said to cause problems when applying styles to controls, since setting the value on the control overrides anything being set by the style, but I will not be bothered by it, since the control implementation isn’t lookless or re-template-able anyway.

It works!

Tagged , , ,