Andrej Tozon's blog

In the Attic

NAVIGATION - SEARCH

MVVM with WPF, Silverlight, and… Windows Forms

In my post-NTK09 conference blog post I mentioned one of my MVVM demos was about sharing a ViewModel between WPF, Silverlight and even Windows Forms application. I got a lot of requests for posting sample code online since then, which I intended to do earlier, but because the sample application code was closely related to the conference, I wanted to create a whole new sample application first and share that. Of course time passed and other things kept me busy from actually doing it. With Silverlight 4 Beta coming out recently, things progressed even further in so I decided to keep things as they were, and post the original sample code. I’ll explain it in few lines (and pictures) here, and the source code can be found here.

What’s it all about?

The purpose of the sample (and my NTK09 talk in general) was about the MVVM pattern principle and how it can be used to totally separate the User Interface from the business logic and data. The application I built was a conference session schedule viewer / editor. Technically, there are three application flavors: WPF, Silverlight and WinForms. They share the same data access (WCF service) and ViewModel. The only thing different between the three is their UI implementation.

imageimageimage

Notice the small arrow overlay over the C# icon on Silverlight and WinForms PageViewModel.cs? That’s because both project are linking the original file from the WPF project, which I created first. That means that ViewModel is exactly the same across all three projects when they are compiled. And with all three project, this is the ViewModel that serves up the main page.

The WPF project

I created the WPF project to ‘just work’. I didn’t bother with styling and the looks, I intentionally left it ugly. I used Prism for commanding and WCF based web service for communicating the data.

image

The ‘Dan’ (Day) and ‘Kategorija’ (Category) serve as the filter for the session list on the left. Each combo box change results in a server request which returns new data for filling the session list. Other fields are editable session details fields. Three buttons on the top left correspond to New, Edit and Delete, in that order.

The Silverlight project

The Silverlight project differ from WPF in that that it’s in color :)

Silverlight

Prism is still used for commanding, but the UI is quite different. I even provided the View (above) and Edit (bottom) screens.

Silverlight Edit

As said, the difference between the WPF and Silverlight applications is just and only different XAML. And both have zero code-behind code. Moving on to…

The Windows Forms project

I  didn’t bother much with making Windows Forms project to work. I last coded anything in WinForms 2+ years ago so I suspect I won’t have to go there again… However, this is the UI I put together:

Windows Forms

It’s very similar to WPF, but data bindings perform much slower. Data bindings? There’s data bindings in WinForms too?

LOL. Sometimes I really do get questions like that. Anyway, let’s see the ‘code behind’. [There’s actually quite a few lines of code behind in Windows Forms because it’s used to instantiate and initialize all controls on the screen. That code is stored in the .designer file and I’m ignoring it as the code behind]

private void MainForm_Load(object sender, EventArgs e)
{
    PageViewModel viewModel = new PageViewModel();

    dayBox.DataBindings.Add("DataSource", viewModel, "Days");
    categoryBox.DataBindings.Add("DataSource", viewModel, "Categories");
    categoryBox.DisplayMember = "Name";
    speakerBox.DataBindings.Add("DataSource", viewModel, "Speakers");
    speakerBox.DisplayMember = "FullName";
    speakerBox.ValueMember = "Id";
    startTimeBox.DataBindings.Add("DataSource", viewModel, "TimeValues");
    endTimeBox.DataBindings.Add("DataSource", viewModel, "TimeValues");
    sessionList.DataBindings.Add("DataSource", viewModel, "Sessions");
    sessionList.DisplayMember = "Title";

    dayBox.DataBindings.Add("SelectedValue", 
        viewModel, "SelectedDay", true, DataSourceUpdateMode.OnPropertyChanged);
    categoryBox.DataBindings.Add("SelectedValue", 
        viewModel, "SelectedCategory", true, DataSourceUpdateMode.OnPropertyChanged);
    sessionList.DataBindings.Add("SelectedValue", 
        viewModel, "SelectedSession", true, DataSourceUpdateMode.OnPropertyChanged);
    speakerBox.DataBindings.Add("SelectedValue", 
        viewModel, "SelectedSession.SpeakerId", true, DataSourceUpdateMode.OnPropertyChanged);

    startTimeBox.DataBindings.Add("Text", 
        viewModel, "SelectedSession.StartTime", true, DataSourceUpdateMode.OnValidation);
    endTimeBox.DataBindings.Add("Text", 
        viewModel, "SelectedSession.EndTime", true, DataSourceUpdateMode.OnValidation);
    titleBox.DataBindings.Add("Text", 
        viewModel, "SelectedSession.Title", true, DataSourceUpdateMode.OnValidation);
    descriptionBox.DataBindings.Add("Text", 
        viewModel, "SelectedSession.Description", true, DataSourceUpdateMode.OnValidation);

    newButton.Click += (s, ev) => 
        { viewModel.NewSessionCommand.Execute(null); };
    editButton.Click += (s, ev) => 
        { viewModel.EditSessionCommand.Execute(viewModel.SelectedSession); };
    deleteButton.Click += (s, ev) => 
        { viewModel.DeleteSessionCommand.Execute(viewModel.SelectedSession); };
    saveButton.Click += (s, ev) => 
        { viewModel.EndEditSessionCommand.Execute(viewModel.SelectedSession); };
    cancelButton.Click += (s, ev) => 
        { viewModel.CancelEditSessionCommand.Execute(viewModel.SelectedSession); };
}

This is all the code for the main form. You’ll notice it’s all used for binding controls to various properties on the ViewModel. I could also get rid of some of the lines by setting the properties on the WinForms designer (thus moving them to the .designer file). The last section wires button clicks to execute commands in the ViewModel.

There is absolutely no business logic in this code which makes it very similar to declarative XAML in WPF and Silverlight projects. I was toying with the idea of making an Extender Provider to get rid of the above lines completely and do it all just with the Windows Forms designer – I made a working prototype, but wasn’t generic enough to include it in the sample.

Source code

Bellow is the full source for all three main and related projects. Also included is the Test project with a few Silverlight Unit Test Framework tests. Database is not included so you’ll not be able to actually run any of the applications. Sorry. I’ll post the Silverlight version online when time permits.



NTK09 – Slide decks from my talks

Had 2 talks at this year’s NT Konferenca.

The first one was about building LOB application with Silverlight, starting from what can we do today with v2 (ran a short, 2 min video of a Silverlight 2 LOB app we’re going to be releasing within few weeks) and what’s coming with v3. The last part was about .NET RIA Services.

The second talk was about Presentation Model (MVVM / Model-View-ViewModel) – from basics to actual working application – starting from a WPF version of an app, then porting the same ViewModel over to Silverlight to build a better UX and thorough testing, to finish with (again, with exactly the same ViewModel) a working version for Windows Forms (yes, that’s WinForms built with MVVM).

Fun.

Update: slides are in Slovenian language…

Paste as Xaml Visual Studio Add-In

Common Windows Presentation Foundation controls don't necessarily provide an intuitive path for upgrading from their Windows Forms equivalents. Take, for example, the RichTextBox control - there is no Rtf property for setting and getting RTF formatted text in WPF's RichTextBox. You can however, still load the RTF text into RichTextBox, using this method (loading RTF text from an embedded resource called MyRtfText):

ASCIIEncoding encoding = new ASCIIEncoding();
byte[] bytes = encoding.GetBytes(Properties.Resources.MyRtfText);

using (MemoryStream s = new MemoryStream(bytes))
{
   
TextRange range = new TextRange(rtfBox.Document.ContentStart, rtfBox.Document.ContentEnd);
   
range.Load(s, DataFormats.Rtf);
}

But how about creating a couple of text lines that would fit right into WPF's RichTextBox? RichTextBox exposes the Document property, which would accept a FlowDocument - and that's a native WPF content element, persisted in Xaml format. If you need to insert a piece of document into RichTextBox at design time, you'll have to know some Xaml to to it. Having to convert some existing text from RTF or other rich text format (pasting from Word seems like a good idea) requires a converter...

Luckily, such a converter is already built in WPF - it's the RichTextBox itself! The following snippet will stuff the text from the clipboard (text format doesn't matter, as long it's text) into the RichTextBox and get it out as a FlowDocument:

RichTextBox box = new RichTextBox();
box.Paste();

string text;
TextRange range = new TextRange(box.Document.ContentStart, box.Document.ContentEnd);

using (MemoryStream stream = new MemoryStream())
{
   
range.Save(TextDataFormat.Xaml.ToString());
   
stream.Seek(0, SeekOrigin.Begin);
   
using (StreamReader reader = new StreamReader(stream))
   
{
        text = reader.ReadToEnd();
   
}
}

And this is the core of this very simple Visual Studio 2008 Add-In that I've created - it will allow you to paste any text from the clipboard into Visual Studio as XAML.

Installing the Add-In is as easy as copying a DLL, included in the zip file [see link below], into *\My Documents\Visual Studio 2008\Addins folder and (re)start Visual Studio 2008. You'll find the new command under Tools menu.

image 

Download the Add-In. Full Add-In source code will follow...

On a side note... WPF future looks bright: performance and VS Designer improvements, new setup framework, new controls (DataGrid, Ribbon, Calendar, ...) See this ScottGu's post for more details.

You've got mail: Windows Forms & WPF interop

One of my favorite samples when talking about WPF is showing a rotating 3D letter 'e' in a Windows Forms application. You know, something like those "you've got mail" computer messages, seen in many Hollywood movies. Windows Forms/WPF interoperability features make this kind of stunt very easy to pull off and this is the recipe to make in happen in 15 minutes...

First, you'll have to come up with some kind of 3D element, which you can import in WPF. I used electric rain's ZAM 3D tool, which has the ability to export 3D elements directly to XAML, which can be later picked up and additionally arranged with  Expression Blend, for example. It actually took me just a couple of minutes to create a 3D letter 'e' and import the scene into Blend.

blendmail1

Rotating the 3D element was done by animating ViewPort's camera by setting 5 progressive key frames of its Rotate Transform property.

image

We need to make the element reusable, so we'll transform it to a user control. Expression Blend 2 (currently still in CTP version) fortunately makes this task very easy by exposing the "Make Control" task (Tools | Make Control...)

OK, half done... We now have a user control with rotating 3D element and a WPF Window, simply displaying it.

The next step is adding a new Windows Form to the project. Save the project in Blend, switch over to Visual Studio (2008) and add a new Form. Visual Studio will make sure all necessary interop assemblies will be added to the project in this process.

With newly created form displayed in the designer area and open a Toolbox. Right at the top (or elsewhere, depending on your project's name), there should be your WPF user control, ready to drag onto the form. When doing so, Visual Studio will create your control, hosted in the ElementHost control (a Windows Forms control, which can host a WPF element). Set control's size to a desired value and you're done.

image

Download the sample project here.

WPF/WinForms Binding Pt.2: Value converters

In previous WPF vs Windows Forms binding post, we left off with pretty useless text binding example - we made the form's title reflect whatever user writes in the textbox:

<Window x:Class="BindingSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding ElementName=textBox1, Path=Text}" Height="300" 
Width="300" Loaded="Window_Loaded"> ...
</Window>

public Form1()
{
    InitializeComponent();
    DataBindings.Add("Text", textBox1, "Text");
}

We'll extend this example to make it more flexible about outputting text. How about specifying a text template like "This form now belongs to {0}", where {0} would be replaced with whatever user writes in her textbox? We'll do that with the help of value converters.

To create a value converter in Windows Presentation Foundation, all you have to do is implement the IValueConverter interface:

[ValueConversion(typeof(string), typeof(string))]

public
class TextFormatter:
IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (targetType != typeof(string)) { throw new ArgumentException("Target type must be string", "parameter"); } return string.Format(parameter.ToString(), value.ToString()); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { throw new NotImplementedException(); } }

The Convert method takes a couple of parameters: value is the actual value, being passed in by binding, targetType is the type we're binding to, parameter in our case will serve for passing in the text template. We'll ignore culture as we don't need to support different locales at this time. Since we're not supporting converting the text back to initial value, we'll leave ConvertBack method unimplemented.

Next, we'll include our value converter in the binding definition.

<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="BindingSample.Window1"
    Height="300" Width="300">
    <Window.Resources>
        <local:TextFormatter x:Key="TextFormatter" />
    </Window.Resources>
    <Window.Title>
        <Binding Path="Text" Converter="{StaticResource TextFormatter}" 
            ConverterParameter="This form now belongs to {0}" 
            ElementName="textBox1" Mode="Default"/>
    </Window.Title>

WPF binding with value converter 

Windows Forms, however, doesn't have something like value converters built in, but you can achieve similar result by using custom classes, which implements similar functionality:

public class SimpleTextFormatter: INotifyPropertyChanged
{
    public string parameter;
    private string value;

    public string Parameter
    {
        get { return parameter; }
        set { parameter = value; }
    }

    public string Value
    {
        get { return value; }
        set
        { 
            this.value = value; 
            OnPropertyChanged("Value"); 
        }
    }

    [Bindable(true)]
    public string FormattedText
    {
        get { return string.Format(Parameter, Value); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

Using this formatter class is again, very simple. First you have to bind form's Text property to formatter's FormattedText property, then bind the input textbox to it's Value property:

public partial class Form1 : Form
{
    private SimpleTextFormatter formatter = new SimpleTextFormatter();

    public Form1()
    {
        InitializeComponent();
       
        formatter.Parameter = "This form now belongs to {0}";
        DataBindings.Add("Text", formatter, "FormattedText");
        textBox1.DataBindings.Add("Text", formatter, "Value", true, DataSourceUpdateMode.OnPropertyChanged);
    }
        ...
}

Again, no events had to be implemented for this to work, it's just the power of data binding, even when used with Windows Forms.

WPF vs. Windows Forms (data) binding

When talking to people about (data) binding in Windows Presentation Foundation, I find that a lot of them are amazed with how easy it is to bind some control's property to another, and the first one being able to react to other's changes. I'm surprised how many people still use data binding in windows forms for just that - binding to data sources, mostly datasets. But, the data can be much more that just some information, contain in data carriers like datasets and other objects.

Let's say you have to write some code to control a control's size with a slider (trackbar). In WPF and XAML, this is trivial:

<Canvas>
    <Slider Height="21" Name="slider1" 
        Width="100" Canvas.Left="178" Canvas.Top="10" 
        Minimum="50" Maximum="200" />
    <Rectangle Canvas.Left="50" Canvas.Top="50" 
        Name="rectangle1" Fill="Red" 
        Height="{Binding ElementName=slider1, Path=Value}" 
        Width="{Binding ElementName=slider1, Path=Value}" />
</Canvas>

Rectangle's Width and Height properties are bound to the value of the slider so when the slider's position changes, the rectangle resizes accordingly. How would you do something like that in Windows Forms?

Event handlers?

private void trackBar1_Scroll(object sender, EventArgs e)
{
    panel1.Width = trackBar1.Value;
    panel1.Height = trackBar1.Value;
}

Sure, why not... [panel1 with Red background is used instead of a Rectangle, and TrackBar is a slider]

But, there's another way, using - you guessed it - binding. Instead of responding to every TrackBar's value change for yourself, you can let the framework do the job:

public Form1()
{
    InitializeComponent();
    panel1.DataBindings.Add("Width", trackBar1, "Value");
    panel1.DataBindings.Add("Height", trackBar1, "Value");
}

Another example: main Form's title should mirror whatever user enters in the TextBox. XAML code:

<Window x:Class="BindingSample.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding ElementName=textBox1, Path=Text}" Height="300" 
Width="300" Loaded
="Window_Loaded"> ...
</Window>

And instead of doing this with Windows Forms:

private void textBox1_TextChanged(object sender, EventArgs e)
{
    Text = textBox1.Text;
}

You could simply do this:

public Form1()
{
    InitializeComponent();
    DataBindings.Add("Text", textBox1, "Text");
}

Now, how about binding to a custom object? For example, we want the size of our rectangle change randomly through time, and come up with a class like:

public class Randomizer
{
    private int number;
    private Timer timer;

    public Randomizer()
    {
        PickNumber();
        timer = new Timer();
        timer.Interval = 500;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Enabled = true;
    }

    public int Value
    {
        get { return number; }
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        PickNumber();
    }

    private void PickNumber()
    {
        number = new Random().Next(50, 201);
// Fixed for simplicity
    }
}

The Value property of this class will change twice per second, each time holding a number between 50 and 200.
Let's bind panel's Width and Height properties to the Value this class produces...

private Randomizer r = new Randomizer();
public Form1()
{
    InitializeComponent();
    panel1.DataBindings.Add("Width", r, "Value");
    panel1.DataBindings.Add("Height", r, "Value");
}

... and run the application... but... nothing happens, the panel doesn't change its size. That's because it doesn't know that Value property ever changes. To let panel's bindings be aware of Value changes, our Randomizer class has to send some notifications to any clients, bound to Value property. We'll do this by implementing INotifyPropertyChanged interface. Here's the whole class again:

public class Randomizer: INotifyPropertyChanged
{
    private int number;
    private Timer timer;

    public Randomizer()
    {
        PickNumber();
        timer = new Timer();
        timer.Interval = 500;
        timer.Tick += new EventHandler(timer_Tick);
        timer.Enabled = true;
    }

    public int Value 
    {
        get { return number; }
    }

    private void timer_Tick(object sender, EventArgs e)
    {
        PickNumber();
    }

    private void PickNumber()
    {
        number = new Random().Next(50, 201); // Fixed for simplicity
        OnPropertyChanged("Value");
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }
}

With INotifyPropertyChanged interface implemented, our rectangle finally starts animating. To conclude, here's how we would use the Randomizer class in WPF:

1. Declare the class as a resource:

<Window.Resources>
    <local:Randomizer x:Key="r" />
</Window.Resources>

2. Bind the rectangle to the Randomizer class, declared as a resource:

<Rectangle Canvas.Left="50" Canvas.Top="50" 
    Name="rectangle1" Fill="Red" 
    Height="{Binding Source={StaticResource r}, Path=Value}" 
    Width="{Binding Source={StaticResource r}, Path=Value}" />

One thing, worth observing here is that rectangle animates even at design time in Visual Studio's IDE, not only at runtime. Enjoy.

Slides from my WPF talks

This is the PowerPoint presentation [in Slovenian language] from my WPF talks I did yesterday and two weeks ago. This was my first time delivering full-day courses and not everything went as originally planned. The first talk suffered from some technical difficulties and classic demos trouble, and for the yesterday, some evil virus got me so there were some fever/sore throat issues, leaving me powerless at the end.

Anyway, I'm not posting any sample code this time, instead I'll be writing some more blog posts on the subject, OK?

Client Application Services - NTK2007 Sample

Attached to this post is the CAS sample I've been presenting on NTK2007, translated to English. Here's couple of notes about setting it up and taking it further:

1. The ("Orcas") solution contains two projects: ClientServices is a web project, serving CAS to the Windows Forms Client (ClientApplication).

2. ClientServices web project doesn't include the Membership database, you'll have to create your own [Visual Studio can create one for you automatically]. With the provided Configuration web tool, set up as many users as you like and create at least two roles: Administrator and PowerUser. Don't forget to switch to Forms authentication.

3. With ClientServices web project set up, configure ClientApplication to use the service. There's a new configuration tab called Services in ClientApplication properties pages. Point all the addresses to the address ClientServices is listening on.

This should get you going... A couple of things to note:

1. Checking the "Remember me" checkbox on the Login dialog acts the same as you'd expect from a web application - the application will automatically log you in on the next run. Log out button will clear this state and force you to enter your credentials manually again.

2. When a user, logging in, is in "Administrator" role, main form's title will reflect that:

Similarly, when a user, not in PowerUser role, clicks the "Say Hello" button, she will get a "No permission" message. This example is showing off declarative code access security.

By default, roles are set up to be cached for 1 day, which means that adding or removing a user from particular role won't have immediate effect on how application handles this role. To change this setting, click on the "Advanced" button on Service's tab:

... just be careful - setting the Role service cache timeout to 0 means that your application will call the service each and every time it needs to check whether current user is in specific role, either demanded imperatively or declaratively.

3. When you declare new profile properties in your profile service, you'll be able to use those as your application properties; just select Load Web Settings on the Settings tab to get them to your client application.

The fun part is that you can bind these properties to your user controls as you were used to with VS2005 applications, except that settings won't be read/written to your local configuration files, but rather to your remote, centralized profile/settings store.

As you can see from the enclosed sample, there's very little code that had to be written for all this to work.

Download the sample code.

Also see some of my previous posts on CAS:

Client Application Services in Windows Forms - The Walkthrough

I was just about to record my first screencast on Client Application Services I was presenting a week ago on local NT Konferenca, when this post popped up in my feeds aggregator - Karl Erickson posted a CAS Walkthrough document, overviewing CAS features, coming with Visual Studio "Orcas".

I started playing with CAS a couple of months ago, when there were virtually no documentation available on this subject, and I'm glad these cool little services are beginning to get a little more attention.

Anyway, if you want to see my screencast anyway, or are just looking for some additional sample code on Client Application Services, post a comment here. I'll be posting my presentation's sample code on my blog shortly.