Peter McGrattan’s Weblog

Silverlight, WCF, ASP.NET, AJAX, Graphics, RIA

Stock List Demo Optimised

Posted by petermcg on July 4, 2008

Edited on 20/October/2008 : Code download updated to support Silverlight 2 RTW

(Code download)

I’ve recently addressed some comments on the blog and also optimised the Stock List Demo sample application, particularly the serialization.  The updates or deltas can now be pushed to clients at intervals from approx 1 millisecond.  If you haven’t seen this demo or haven’t downloaded it in a while it’s worth taking a look at the latest version.  Here is a screenshot of the server and one client application running :

Application Running

The sample comprises a Silverlight application with a DataGrid bound to an ObservableCollection<T> initially populated from a call to a Wcf Service and then updated by deltas received over Tcp Sockets.  The sample as a whole provides code that demonstrates some interesting different areas of Silverlight 2 development.  Silverlight Sockets support is used to push updates to one or many connected clients now at intervals from approx 1 millisecond upwards (the sample defaults to 50 milliseconds but feel free to change this to speed the sample up or slow it down) :

// Server
private void ServeDeltas()
{
    while (true)
    {
        if (synchClients.Count > 0)
        {
            // Send latest delta to all subscribed clients
            synchClients.SendAll(GetLatestDelta());

            // Set this to anything from 1.0 millisecond
            Thread.Sleep(TimeSpan.FromMilliseconds(50.0));
        }
        else
        {
            clientEvent.WaitOne();
        }
    }
}

The Silverlight UI remains responsive and notes can still be added against stocks in the DataGrid at these speeds.  The serialization has been optimised to use the DataContractSerializer and MemoryStream classes to more efficiently and reliably serialize and deserialize Stock objects from server to client :

// Server
public void Send(Stock stock)
{
    byte[] bytes = new byte[1024];

    using (MemoryStream memStream = new MemoryStream(bytes))
    {
        xmlSerializer.WriteObject(memStream, stock);

        try
        {
            memStream.WriteTo(netStream);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

// Client
private void OnSocketAsyncEventCompleted(object sender, SocketAsyncEventArgs e)
{
    Socket socket = e.UserToken as Socket;
    byte[] buffer = new byte[1024];

    switch (e.LastOperation)
    {
        case SocketAsyncOperation.Connect:
            // Set up receive buffer
            e.SetBuffer(buffer, 0, buffer.Length);
            // Asynchronously request to receive data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.None:
            break;
        case SocketAsyncOperation.Receive:
            // Deal with received data from the server in our buffer
            int bytesTransferred = e.Buffer.TakeWhile(b => b != byte.MinValue).ToArray().Length;

            using (MemoryStream memStream = new MemoryStream(e.Buffer, 0, bytesTransferred, false))
            {
                try
                {
                    Stock stock = xmlSerializer.ReadObject(memStream) as Stock;

                    Action<Stock> action = UpdateStockList;
                    owner.BeginInvoke(action, stock);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.ToString());
                }
            }

            // Clear receive buffer
            e.SetBuffer(buffer, 0, buffer.Length);

            // Asynchronously request to receive more data from the server
            socket.ReceiveAsync(e);
            break;
        case SocketAsyncOperation.Send:
            break;
        default:
            break;
    }
}

The sample also shows how to use the Silverlight Dispatcher and Action<T> to queue work for execution on the UI thread (see client code after ReadObject above) and also how to apply an Action<T> to each element in a List<T> using ForEach :

// Client
private void OnGetStocksCompleted(object sender, GetStocksCompletedEventArgs e)
{
    Action<Stock> action = delegate(Stock delta)
    {
        StockDecorator stock = stockList.FirstOrDefault(s => s.Symbol == delta.Symbol);

        if (stock == default(StockDecorator))
        {
            stockList.Add(new StockDecorator(delta));
        }
    };

    e.Result.ForEach(action);
}

The Silverlight Socket policy requirements are handled on a separate worker thread in the same server that sends the Stock updates :

// Server
private void ServePolicy()
{
    while (true)
    {
        Console.WriteLine("Accepting Policy Requests...");

        // Read policy file
        byte[] policyBytes = File.ReadAllBytes("PolicyResponse.xml");

        using (Socket client = policyListener.AcceptSocket())
        {
            Console.WriteLine("Policy Request Accepted...");

            // Get poilicy request header
            byte[] buffer = new byte[1024];
            int bytesReceived = client.Receive(buffer);

            // Basic check of request header
            string header = Encoding.UTF8.GetString(buffer, 0, bytesReceived);

            if (header == "<policy-file-request/>")
            {
                client.Send(policyBytes, 0, policyBytes.Length, SocketFlags.None);
            }
        }
    }
}

The animation of the Stock Updates (green for positive, red for negative) is created programmatically and the sample also shows how to bind to those animations from Xaml :

<data:DataGrid x:Name="StocksGrid"
               AutoGenerateColumns="False"
               Grid.Row="1"
               Grid.ColumnSpan="2"
               GridlinesVisibility="Horizontal"
               HeadersVisibility="Column"
               RowBackground="AliceBlue"
               IsReadOnly="True"
               CanUserResizeColumns="False"
               Margin="0,5,0,0">
    <data:DataGrid.Columns>
        <data:DataGridTemplateColumn Header="Symbol">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Symbol}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Bid">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding BidHighlight}">
                        <TextBlock Text="{Binding Bid}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn Header="Ask">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Background="{Binding AskHighlight}">
                        <TextBlock Text="{Binding Ask}" />
                    </StackPanel>
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
        <data:DataGridTemplateColumn MinWidth="150" Header="Notes">
            <data:DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Notes}" />
                </DataTemplate>
            </data:DataGridTemplateColumn.CellTemplate>
        </data:DataGridTemplateColumn>
    </data:DataGrid.Columns>
</data:DataGrid>

Multiple clients from different browsers can be connected by firing up a new browser and copy/pasting the address, they will receive the same updates (notice the highlights in the screenshot below) :

Multiple Clients Connected

The server will pause using an AutoResetEvent if there are no clients connected and resume when at least one client connects (you can also pause the server by pressing the pause key on your keyboard and resume by pressing Enter).  If you are working on a proof on concept for Silverlight 2 hopefully this is an example of a substantial application which may be helpful.

Advertisements

12 Responses to “Stock List Demo Optimised”

  1. […] this came about in the context of his Silverlight Desktop and if you haven’t seen that… go do so! Stock List Demo Optimised Peter McGrattan has not only updated his Stock List demo, but has optimised it as well… this has […]

  2. Frank Patton said

    Have you done any testing of WCF Silverlight VS2008 SQL (Northwinds or any other SQL Table)

    I cant get my local machine to address the web service ..

    Thanks

  3. Peter McGrattan said

    Hi Frank,

    There are several tutorials available that show how to access data from a SQL table in a Silverlight application, have a look here for a good step-by-step tutorial that uses the AdventureWorks sample database.

    Regards,

    Peter

  4. Richard said

    Hi Peter,

    excellent website and resource – thank you.

    I have downloaded the code and opened the solution. The only problem I have is that the grid does not automatically update (if I change the external XML file) I have to manually refesh the page to see the change and then the grid does not show the colour background.

    Any ideas? Is there something I need to start?

    Thank you

    Richard

  5. Richard said

    Hi Peter

    bit of a schoolboy error – didn’t start the Delta Server.
    Excellent example, thank you.

    Would you know how to display these stocks one at a time on the screen? So you display the details for AAHH 296.02 256.7 (for a few seconds) then you display AAPM 181.31 182.03 for a few seconds until the end of the xml file and then start again?

    Many thanks

    Richard

  6. Peter McGrattan said

    Hi Richard,

    Thanks for the comments, looks like you’ve got the sample working fine now.

    Regards,

    Peter

  7. Mari said

    Hi,
    I want to convert this project to vb.net , please if you have a vb.net version of this project it will be very helpful for me because I found many problems in conversion,
    Can you explain and convert me this syntax for example:

    return proxy.GetStocks().OrderBy(s => s.Symbol).ToList();

    StockDecorator stock = stockList.FirstOrDefault(s => s.Symbol == delta.Symbol);

    thanks

  8. Great App! Appreciate the contribution…

  9. Kapil Bhavsar said

    Great!

    Can you help me on compression … i want the data to be compressed while sending…

  10. mabra said

    Hello !

    Looks like a very impressive demo [I saw the pollingDuplex too]. But I cannot make this solution running and so, I wish some help.

    The SL client starts and initially loads data, but no updated. I thought – but was not sure – I have to run the StockDeltaService too, but this fails. If I start it before the webserver, it fails [tcp connection refused]. If I start it after the webserver, if failes too [Only one usage of each socket address allowed]. So what could go wrong here?

    Any help is very welcomed [I am not a VS expert and no WCF experience and even SL is new for me].

    br–mabra

  11. mabra said

    Hi !

    It’s me again, but with good new 😉
    There was another tcp demo hunging around in mem …. 😦
    After a restart, everythings works very well!

    Thanks again for this great demo!!!

    br–mabra

  12. sandrar said

    Hi! I was surfing and found your blog post… nice! I love your blog. 🙂 Cheers! Sandra. R.

Sorry, the comment form is closed at this time.

 
%d bloggers like this: