Locations

Blog Stats

Archive for the ‘WPF’ Category

Introduction

This post presents an early prototype of ‘LINQ to Visual Tree’ for Silverlight 2 and WPF applications. The ultimate goal is to provide applications with the ability to quickly and easily query the Silverlight 2 or WPF Visual Tree via LINQ to Objects.

The prototype provides this functionality by enabling enumeration of an application’s in-memory Visual Tree through the IEnumerable<T> interface. This is distinct to implementing a custom query provider: the prototype does not implement IQueryable<T> or execute and translate expression trees.

This post demonstrates the basics so far and makes the prototype available for download, it’s aim is to get feedback and ideas on how the simple API can be improved.

Trees in Silverlight and WPF

The one fundamental tree of objects that make up a Silverlight 2 or WPF application is the object tree. WPF provides two conceptual views of the object tree called the logical tree and the visual tree that if combined would expose the full object tree. The two conceptual trees are exposed via the LogicalTreeHelper and VisualTreeHelper static classes respectively. The logical tree is a map of elements in the object tree’s element hierarchy (defined in Xaml or in code) that make up an applications user interface, including inferred elements not explicitly typed by the programmer. The visual tree is a map of elements in the object tree’s element hierarchy that derive from Visual or Visual3D, each element is exploded into it’s constituent visual ingredient elements.

Silverlight does not expose the full object tree, there is more than likely a logical tree behind the scenes but there is currently no LogicalTreeHelper static class to expose it. Silverlight does however expose a visual tree through VisualTreeHelper but the Visual and Visual3D types do not exist in the API. The criteria for an object’s inclusion in the Silverlight visual tree according to MSDN is that the object has a "rendering implication".

LINQ to Visual Tree

So every Silverlight and WPF application has a Visual Tree exposed through VisualTreeHelper. ‘LINQ to Visual Tree’ extends this model by enabling enumeration of the results returned from VisualTreeHelper for an object and it’s descendants. The prototype is exactly two days old – I’m still experimenting at this stage, it currently consists of the following two extension methods:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Windows;

using System.Windows.Media;

namespace LinqToVisualTree.Common

{

publicstaticclassVisualEnumerable

{

///<summary>

/// Gets the Visual Tree filtered by Type for a DependencyObject with that DependencyObject as the root.

Although there are currently only two extension methods, they provide an abundance of scope for querying the Visual Tree in Silverlight or WPF. Let’s take a look at some usage scenarios from the code download to see how ‘LINQ to Visual Tree’ can be used in practice to query the Visual Tree of a Silverlight 2 application consisting of the following simple Xaml:

Xaml similar to the above is also used for the test WPF application in the code download, for testing it’s really the hierarchy of the elements in the Xaml that is important – this can be visualized as follows:

The code download defines seven tasks that demonstrate how to use the ‘LINQ to Visual Tree’ API, starting off with basic queries and progressing to slightly more involved ones. Each task shows the code to produce the required result using both Extension Method syntax and LINQ syntax. Each task is represented by a method called from the Page_Loaded event handler defined in the above Xaml:

///<summary>

/// Task 1: Get the Visual Tree of this UserControl

///</summary>

privatevoid Task1()

{

// Extension Method syntax

var pageTree1 = this.GetVisualTree();

// Linq syntax

var pageTree2 = from v inthis.GetVisualTree()

select v;

Debug.Assert(pageTree1.SequenceEqual(pageTree2));

}

Silverlight Result

WPF Result

///<summary>

/// Task 2: Get all the TextBox controls in this UserControl's Visual Tree

Feel free to download the code and run the tasks to see how the API is used from both Silverlight and WPF. As I say I’m still experimenting with this so I’d love to hear your suggestions and get feedback and ideas on how the simple API can be improved.

Introduction

This post attempts to highlight some of the intricacies involved in connecting a WPF Client to the existing Silverlight 2 Polling Duplex demo previously published on this blog. To recap, the demo consists of a WCF service designed to push Stock data to previously connected Silverlight 2 clients over Http. In a similar scenario to a chat application, the Silverlight 2 clients can also send ‘Note’ messages back to the server and have them propagated to other connected clients.

The aim of this post is to make the demo capable of also supporting connections from WPF clients. Success is defined by enabling WPF clients to connect to the same WCF service, receive the same Stock updates at the same time as the Silverlight 2 clients and having Notes synchronized across all connected clients regardless of their underlying technology. Below is a link to a screenshot of the final version of this demo running, shown is the Console Application hosting the WCF service with two Silverlight clients and one WPF client connected:

PollingDuplexHttpBinding and WPF clients

Up until this point the demo application has solely supported pushing data to Silverlight 2 clients, defining a PollingDuplexHttpBinding endpoint on the server side and creating a channel factory as part of the Silverlight 2 client as shown below:

However, attempting to use PollingDuplexHttpBinding for this purpose in a WPF application (as in the code above) currently results in a NotSupportedException exception being raised with a full explanation and even some architectural advice:

PollingDuplexHttpBinding cannot currently be used in non-Silverlight clients without running into this exception. In order to extend the demo to support pushing Stock data to a WPF client through Http the guidance from the above exception is to consider WSDualHttpBinding. This binding predates Silverlight and is the closest alternative for achieving equivalent results to PollingDuplexHttpBinding in a non-Silverlight client application. A look at the BindingElement objects each binding encapsulates reveals WSDualHttpBinding is actually a much more mature binding than PollingDuplexHttpBinding:

WSDualHttpBinding

PollingDuplexHttpBinding

TransactionFlowBindingElement

PollingDuplexBindingElement

ReliableSessionBindingElement

TextMessageEncodingBindingElement

SymmetricSecurityBindingElement

HttpTransportBindingElement

CompositeDuplexBindingElement

OneWayBindingElement

TextMessageEncodingBindingElement

HttpTransportBindingElement

The WSDualHttpBinding Endpoint

The outcome of acting on the aforementioned advice in the demo application is the addition of a new endpoint to the WCF service exposed from the StockServer Console Application. This could easily be specified entirely in a configuration file (see this post for how to configure the Silverlight policy and duplex endpoints) but in the download is achieved in code as shown below:

Although the WSDualHttpBinding endpoint is registered using a different URI, the endpoint is simply just another entry point into the same WCF service called by the existing Silverlight 2 clients using PollingDuplexHttpBinding. Before you can push data from a WCF duplex service to a client, the client must initiate the session by calling the service. The Register method serves this purpose in the demo application and the only change to the code in the WCF service itself (although there are no method signature changes) is in this method as shown below:

The change is minor but essential: extracting the MessageVersion from the initial message sent by the client and passing it to the constructor of the StockClient object along with a reference to the channel back to that client. The variety of MessageVersion used by each of the bindings in the demo application is as follows:

Binding

MessageVersion

WSDualHttpBinding

MessageVersion.Soap12WSAddressing10

PollingDuplexHttpBinding

MessageVersion.Soap11

Messages sent between WSDualHttpBinding endpoints use the SOAP 1.2 protocol; between PollingDuplexHttpBinding endpoints the SOAP 1.1 protocol is employed. There is a clue in the name of MessageVersion. Soap12WSAddressing10 that WSDualHttpBinding also requires addressing headers to support reliable messaging sessions via ReliableSessionBindingElement. In the case of these two bindings, attempting to use a mismatch of MessageVersion and binding, for example MessageVersion.Soap11 for messages sent to a WSDualHttpBinding endpoint, results in an InvalidOperationException as shown below:

Storing the message version along with the channel reference in an instance of the StockClient class (see code download) ensures the reply messages are pushed back to the client in the format the relevant binding expects. The rest of the WCF service remains the same: a new instance of StockService is created per session regardless of the endpoint used. Each session registers it’s interest in the DeltaReceived event of the singleton StockGenerator class. This event is raised approx every 250 milliseconds and each session’s handler (OnDeltaReceived) sends the same generated delta to its respective client via the stored channel reference using the appropriate message protocol.

The WPF Client Application

In a good advertisement for how nearly all of what you write in Silverlight 2 can be used without modification in WPF, the code in the WPF client application is extremely similar to that contained in the Silverlight 2 client project. The markup in StockWindow.xaml and code in StockWindow.xaml.cs is virtually identical to Page.xaml and Page.xaml.cs in the Silverlight 2 project.

One difference between the WPF and Silverlight client code is the WPF DataGrid which is from the October 2008 Release of the WPF Toolkit available from codeplex. Another difference already highlighted is that PollingDuplexHttpBinding cannot be used in a non-Silverlight client application. In its place we use WSDualHttpBinding on the WPF client side also and due to this binding’s maturity the code in StockTicker.cs is less verbose as shown below:

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Windows.Threading;
using Common;
namespace WpfStockClient
{
public sealed class StockTicker : IStockClient
{
// Reference to layout instance that created the StockTicker
private readonly Dispatcher owner = null;
// Serializes instances of the Stock type before they are sent on the wire
private readonly DataContractSerializer stockSerializer = new DataContractSerializer(typeof(Stock));
// Proxy for communication to WCF service
private IStockService proxy;
// List of stocks designed to be bound to a UI control
private readonly StockList stockList = new StockList();
public StockList StockList
{
get { return stockList; }
}
public StockTicker(Dispatcher owner)
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
this.owner = owner;
}
public void SubscribeDeltas()
{
EndpointAddress endPoint = new EndpointAddress("http://localhost:10201/WpfStockService");
proxy = new DuplexChannelFactory<IStockService>(this,
new WSDualHttpBinding(WSDualHttpSecurityMode.None),
endPoint).CreateChannel();
proxy.Register(Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "StockClient/IStockService/Register"));
}
public void Receive(Message message)
{
Stock stock = message.GetBody<Stock>();
// Queue a call to UpdateStockList on the Dispatcher of the thread that created this instance
Action<Stock> action = UpdateStockList;
owner.BeginInvoke(action, stock);
}
public void Sync(Stock stock)
{
// Create a message with the appropriate SOAPAction and asynchronously send it via the proxy with the serialized Stock as the body of the envelope
Message message = Message.CreateMessage(MessageVersion.Soap12WSAddressing10, "StockClient/IStockService/Sync", stock, stockSerializer);
proxy.Sync(message);
}
private void UpdateStockList(Stock delta)
{
// NOTE : CheckAccess is there but intellisense doesn't see it because of the [EditorBrowsable(EditorBrowsableState.Never)] attribute
if (!owner.CheckAccess())
{
throw new InvalidOperationException("The calling thread cannot access this method because a different thread owns it.");
}
// Check if this Stock is already in the collection
Stock existing = stockList.FirstOrDefault(s => s.Symbol == delta.Symbol);
if (existing == default(Stock))
{
// This delta is a new Stock
stockList.Add(new StockHighlight(delta));
}
else
{
// This delta is an existing Stock
existing.Ask = delta.Ask;
existing.Bid = delta.Bid;
if (!String.IsNullOrEmpty(delta.Notes))
{
existing.Notes = delta.Notes;
}
}
}
}
}

There are many similarities between the above code and the Silverlight 2 version; the changes are all centred on the body of the SubscribeDeltas method. This is where the client instance of the WSDualHttpBinding class is instantiated and used to establish a composite duplex channel (two one-way channels) with the new endpoint at the address exposed in the WCF service. The first parameter passed to the DuplexChannelFactory<IStockService> constructor specifies ‘this’ as the instance of the IStockService interface to receive the pushed messages from the WCF service in the Receive method. After the channel is created the initial message is sent using the correct MessageVersion as described earlier.

Linked Common Files

Those class files that are identical across both client projects are added to the Common Solution folder and are referenced using links from the respective projects where they are used:

The highlighted classes are written in one place and as such are assured of being the same structure throughout the solution (and where relevant on both sides of the wire as a consequence). As the files are linked, the code they contain will be compiled into IL that targets the respective version of the CLR implied by the type of project the classes are linked from. For the WPF project the linked classes will be compiled into an assembly that targets the desktop CLR, for the Silverlight project the linked classes will be compiled into an assembly that targets the more compact CoreCLR. Linking common files in this manner is one solution to the problem normally solved by using a class library when a common CLR is targeted by all projects.

Animating the Stock Price changes

Illustrating changes to Stock price instances displayed in the DataGrid by creating the illusion that the DataGrid cell background is changing color takes less code in WPF than in Silverlight. The code for this animation for both WPF and Silverlight 2 is contained in the StockHighlight class. The entire contents of the StockHighlight.cs file is shown below, the code between the #if and #endif directives is compiled only if the SILVERLIGHT symbol is defined as it is in the Build tab of project properties on all default Silverlight 2 projects:

The targets of the animation are the two public SolidColorBrush properties, AskHighlight and BidHighlight. The Color dependency property of these brushes is animated using color key frame animation from green for a positive change or red for a negative change. The animation for both WPF and Silverlight begins with a call to the BeginHighlight method when the corresponding Ask or Bid price property changes. The SILVERLIGHT debug symbol makes it clear to see the extra Storyboard objects and extra initialization required in the InitializeAnimation method to achieve the desired effect in Silverlight 2. You can see the references to these AskHighlight and BidHighlight properties in the WPF XAML below, also notice the xmlns:data for the DataGrid from the WPF Toolkit:

Compatibility should still be maintained however as this namespace or the namespace defined in WPF 3.0 can be used when building WPF 3.5 applications.

XML Namespaces

An XML namespace must be a valid Uniform Resource Identifier (URI), a URI can either be a Uniform Resource Locator (URL) as in the above examples or a Uniform Resource Name (URN) as in the example below :

xmlns:charts="clr-namespace:Charts;assembly=Charts"

These URI’s do not resolve to any useful resource if you put them in the address bar in Internet Explorer for example, their only job is to be unique. A common way to guarantee that uniqueness throughout the XAML’s travels is to start by using a domain name previously registered with an Internet Naming Authority such as schemas.microsoft.com and add a custom suffix such as netfx/2007/xaml/presentation.

The Default Namespace

There can be only one default XML namespace for an element, any additional namespaces must use a prefix. In the default XAML markup below, the URI specified by the xmlns identifier without any prefix is the default namespace for the UserControl element and it’s descendant elements (the namespace’s scope) but default namespaces have no affect on attributes. This means that all unqualified element names within the default namespace’s scope (UserControl and Grid) are implicitly associated with that namespace’s URI.

This is important because it is this implicit namespace URI that the XAML processor uses to search referenced assemblies to find the matching CLR Type for these unqualified elements. The image below shows the default references for a standard Silverlight Application containing the above markup :

These referenced assemblies are searched by the XAML processor for a predefined, hardcoded attribute (the XmlnsDefinition attribute) defining this implicit URI as it’s ‘key’ :

When found, as many are in the System.Windows.dll assembly only, the name of a CLR namespace is retrieved from the ‘value’ of the same attribute. This CLR namespace is then returned as a scope for the XAML processor to search within to find the matching CLR Type for the XAML element. Both the UserControl and the Grid types are found in the System.Windows.Controls CLR namespace in this way :

You can see from the illustration above that twelve CLR namespaces in the System.Windows.dll assembly are mapped to the Silverlight Beta 2 default XAML namespace and that the Beta 1 namespace is currently still supported. Note that this process is also the method by which the CLR Application type, instantiated by the the Silverlight plug-in control, is mapped :

Using the default XML namespace on the Application element in App.xaml, this fundamental CLR type is found in the System.Windows namespace again in the System.Windows.dll assembly :

Prefixed Namespaces

To recap, there can be only one default XML namespace for an element, any additional namespaces must use a prefix and default namespaces have no affect on attributes. In order to affect an attribute with a namespace a prefix must be used, the prefix then becomes an abbreviation for that namespace. In this way the x prefix is used by convention with it’s corresponding pre-defined URI to fulfil Silverlight’s contract as an implementation of the XAML language :

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

The XAML language expects implementers of it’s syntax to expose certain fundamental attributes such as x:Class and x:Name (as show in the examples above) so the processor can perform duties such as joining a code-behind file to a XAML file through a partial class and creating members of that class with meaningful names. This functionality is manifest in the System.Windows.Markup namespace also in the System.Windows.dll assembly.

For more information on XML namespaces in general and how they affect the family of XML technologies, have a look here and here.