Geeks With Blogs
Kevin Rohling Building great s0ftware, 1 line at a time.

Download Sample Code

The Problem

Now that we have Silverlight and WPF the line between Web Applications and Smart Clients is getting smaller and smaller. Despite some marked differences between these two platforms they both support XAML, they both can run our compiled C# code, both can call web services, and we even get our beloved Visual Studio development experience on both sides too! With so many similarities the temptation to build applications that span both platforms is almost too sweet to resist. Nevertheless, the rulebook on how to maximize reuse hasn’t been written and more often than not the copy & paste experience won’t get us very far.

Enter the View Model

With the advent of XAML a new presentation pattern was born out of the MV* family. Model-View-ViewModel (MVVM) has been embraced by much of the WPF community as the presentation pattern of choice, and for good reason. One very attractive aspect of MVVM is that it leverages the data binding runtime native to WPF and (to a more limited degree) Silverlight as well to handle the “Presentation Plumbing”. The net result is that there is very rarely a need to write presentation-specific code. Instead we simply expose properties on our ViewModels and data bind them to our Views (XAML), Voila magic! Beyond the obvious coolness of letting the runtime do the presenting for you, other benefits include:

  • · A fully “Testable” UI implementation
  • · Fully separated Designer/Developer experiences
  • · *Presentation-Agnostic code that is easier to reuse between platforms

mvvm

Stack Diagram showing the direction of dependencies

within the MVVM Presentation pattern

 

For more information on MVVM:

Working M-V-VM Into Silverlight

Because we’re using the Model-View-ViewModel pattern we need to fill in some of the gaps that are missing in Silverlight to make all of the databinding goodness we need work.

First, much of MVVM’s elegance comes from the WPF Command infrastructure. While Silverlight does have the ICommand interface there are no implementations and also no supporting infrastructure, i.e. no Command property on Button. Fortunately, the Patterns and Practices folks included this support in Prism 2.0 using Attached Properties:

   1:  xmlns:cal="clr-namespace:Microsoft.Practices.Composite.Presentation.Commands;assembly=Microsoft.Practices.Composite.Presentation"
   2:  <Button cal:Click.Command="{Binding Path=Command}" Content="{Binding Path=DisplayName}" DataContext="{Binding Path=MyCommand}" />

 

Another “really nice to have” feature from WPF is Implicit Data Templating. This allows us to associate a Data Type with a Data Template in our resources so that when we bind data of that type later in our Xaml the runtime is able to use this information to figure out the correct Data Template to use. This can be very useful, especially when binding data of different types to an ItemsControl. While this feature is missing in Silverlight it can be “faked”. One way we can achieve this behavior, is by using a custom IValueConverter to find the correct DataTemplate.

   1:  <ContentControl Content="{Binding Path=ActiveWorkspace}" ContentTemplate="{Binding Path=ActiveWorkspace, Converter={StaticResource DataTemplateConverter}}" />

 

In this example we are binding the ContentTemplate property of this control to its Content and specifying a DataTemplateConverter as the IValueConverter to use. The DataTemplateConverter will look at the Data Type it has been bound to and use that information to find and return the appropriate Data Template. In this implementation the heuristic is very simple: take the Data Type name, postfix it with “.DataTemplate” and use this as a Resource Dictionary key. This certainly gets the job done but there are likely cleaner ways of achieving the same result.

Getting Started with Multi-Targeting

To get started we’ll need to set up Multi-Targeting, this allows the compiler to build the same source code out to different platforms. Because WPF cannot reference Silverlight projects and vice versa, the goal is the reuse of our Source Files and not Class Libraries in the traditional sense.

In this example I have created separate View, ViewModel, Model and Service Façade projects for WPF and Silverlight. While the differences between these two platforms make sharing Views (XAML) an unlikely prospect, the remaining projects are likely to see a high degree of overlap and are therefore good candidates for multi-targeting.

projects

  • · If you’re starting off fresh I strongly recommend installing Project Linker, which is distributed as part of the Composite Application Guidance for WPF and Silverlight. This tool will map Source and Destination projects so that any file that is added to the Source project will be automatically linked to the Destination.
  • · If you have existing code then go to the Add Existing Item context menu on your Destination project, select the code files to multi-target and choose Add As Link. Linking allows the source for both projects to stay in one place.
  • · It is a best practice to use your Silverlight project as the Source as this will reduce the chances of writing non-portable code.
  • · It is also a good idea to change projects that are sharing code to have the same Namespace. This will reduce the chances that your solution will become a hodgepodge of namespace references.

Making It Fit

Now that projects are all linked up we can start happily hacking away, sublime in the knowledge that when we select Build we’ll get double our money back. Until it happens… “I really need to do something one way in WPF and another way in Silverlight!” A good example is IDataErrorInfo which does not exist in Silverlight but can be useful for displaying Validation information in WPF. Another example is ObservableCollection<T> which has different constructors with different performance implications. No matter how it happens one thing is for certain: It will happen eventually.

There are two approaches I’ve found that work very well in different situations. The first involves using #if compiler directives:

   1:  #if SILVERLIGHT
   2:              this.AllQuestions = new ObservableCollection<QuestionViewModel>();
   3:              questionViewModels.ForEach(i => this.AllQuestions.Add(i));
   4:  #else
   5:              this.AllQuestions = new ObservableCollection<QuestionViewModel>(questionViewModels);
   6:  #endif

 

This tells the compiler to build slightly different code depending on the platform. This is probably ideal for cases where there are only a couple lines difference. For more extreme cases, or when the solution won’t fit inside an #if, partial classes will likely do the job. In the following example I used a partial class to implement the IDataErrorInfo interface. This partial class is added directly to the WPF project and does not exist in the Silverlight code.

   1:      public partial class NewQuestionViewModel : IDataErrorInfo
   2:      {
   3:      }

 

Building a Common Service Façade Interface

In the previous section we looked at some ways we can configure Silverlight to easily bind to ViewModel classes. However, for most LOB applications this is only half of the equation. It’s great that we can present data but where’d the data come from in the first place? Well, chances are, at least for Silverlight applications, it’s probably coming from a web service. This is significant to a multi-targeted solution because web services in Silverlight function only asynchronously. This event-driven model is very different than what most developers are used to working with in richer platforms like WPF. Nevertheless, we would like the same code to be able to interact with an asynchronous web service running in Silverlight and a synchronous mechanism like a Business Façade when running on a smart client.

The trick to making this work properly is defining an interface that can be implemented in both scenarios. Rather than trying to make Silverlight behave synchronously, it is far more advisable to make our WPF implementation at least appear to be asynchronous. In this example I have followed the pattern set forth in Silverlight’s web service proxy model by defining events that deliver operation specific information: Completion notification as well as result and exception delivery.

   1:  public interface IServiceFacade
   2:  {
   3:   event OperationCompletedDelegate ChangeAnswerCompleted, CreateAnswerCompleted, CreateQuestionCompleted, RemoveQuestionCompleted;
   4:   event OperationCompletedDelegate<IList<Answer>> RequestQuestionAnswersCompleted;
   5:   event OperationCompletedDelegate<IList<Question>> RequestQuestionsCompleted;
   6:   
   7:   void ChangeAnswer(QuestionAndAnswer.UI.Model.Answer answer);
   8:   void CreateAnswer(QuestionAndAnswer.UI.Model.Question question, QuestionAndAnswer.UI.Model.Answer answer);
   9:   void CreateQuestion(QuestionAndAnswer.UI.Model.Question question);
  10:   void RemoveQuestion(QuestionAndAnswer.UI.Model.Question question);
  11:   void RequestQuestionAnswers(QuestionAndAnswer.UI.Model.Question question);
  12:   void RequestQuestions();
  13:  } 

An interface of this style can be implemented either synchronously or asynchronously allowing consumers to remain portable across platforms.

Summary

In conclusion, the degree of reuse required will depend highly on the context of the project. In some cases the functionality slated for different platforms may be too different to make multi-targeting a worthwhile effort. There is also the trade off of added complexity that comes whenever you add an #if to your source code, create a one-off partial class that is platform specific, or switch to an event driven Service Façade model. Nevertheless, there are substantial benefits to be had from writing portable code that can be targeted for both the WPF and Silverlight platforms and for projects that need this degree of reuse there are methods that make this possible.

Posted on Sunday, March 1, 2009 4:49 PM | Back to top


Comments on this post: MultiTargeting M-V-VM: An Exercise in reuse for Silverlight and WPF Applications

# re: MultiTargeting M-V-VM: An Exercise in reuse for Silverlight and WPF Applications
Requesting Gravatar...
Great stuff! Thanks for sharing.

Regards,
Software
http://www.sblsoftware.com/embedded-robotics.aspx
Left by E-Governance Solutions on Mar 02, 2009 6:42 PM

# DataTemplateConverter with ListBox
Requesting Gravatar...
I'm trying to figure out how to use multiple DataTemplates with a ListBox in Silverlight. How would I go about hooking up the DataTemplateValueConverter you've created here to work in this situation?

I'm just not sure where to reference it.

Thanks,

Brian
Left by Brian Vallelunga on Mar 11, 2009 10:51 AM

# RE: DataTemplateConverter with ListBox
Requesting Gravatar...
Hey Brian,
There's no good way that I've found to do this inline on the ListBox. My solution has been to set the ItemTemplate to a content control and reference the DataTemplateValueConverter from the ContentControl:

<ItemsControl ItemsSource="{Binding Path=Commands}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplate="{Binding Converter={StaticResource DataTemplateConverter}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Left by Kevin Rohling on Mar 12, 2009 5:49 AM

# re: MultiTargeting M-V-VM: An Exercise in reuse for Silverlight and WPF Applications
Requesting Gravatar...
Hi Kevin,
First this is a great post that has really helped me out a lot in porting my wpf app to silverlight.

I keep running into trouble when adding the Path attribute in the ContentTemplate Binding. It works some of the time and gives me a white screen a lot of the time. I have spent a couple of days trying to figure out the exact behavior, but no luck yet.

Just wondering if you have run into any simliar problems. By the way I am using siverlight 3 beta.

Thanks so much, Lee
Left by Lee on Apr 17, 2009 11:52 AM

# re: MultiTargeting M-V-VM: An Exercise in reuse for Silverlight and WPF Applications
Requesting Gravatar...
Hello Lee and I'm glad this article has been useful.
If you are using the DataTemplateConverter the Path argument for the ContentTemplate Binding should be the same as any Path argument you are using in the Content Binding. For example:
<ContentControl Content="{Binding Path=MyContent}" ContentTemplate="{Binding Path=MyContent Converter={StaticResource DataTemplateConverter}}"/>

If you are still having trouble and can post the XAML I'd be happy to take a look.
Left by Kevin Rohling on Apr 18, 2009 12:50 PM

Your comment:
 (will show your gravatar)


Copyright © SundriedCoder | Powered by: GeeksWithBlogs.net