Blendability Part I – Design time ViewModel

December 17, 2010

In case that you haven’t added the word ‘Blendability’ to your XAML Jargon yet, I’m sure this post will inspire you doing so.

The Blend-ability term describes how a piece of data model or view model is viewable or designable at design time, whether by Expression Blend or Visual Studio Designer.

Building a Silverlight or WPF applications, everybody loves using the MVVM pattern. This pattern greatly decouples the view from its logic and domain model, hence enabling relatively easy unit testing and provide great flexibility. In most cases there should be no code behind XAML except for the InitializeComponents in the view’s ctor (in case of UserControl). This very important aspect of the MVVM pattern should help the XAML designer changing the view using any favorite tool, such as Blend, without being care about the view’s logic.

Let’s have a look on a little application which uses the MVVM pattern for displaying a simple view.

public class Camera2dInspectionViewModel : NotificationObject
{
   
public ObservableCollection<DefectViewModel> Defects { get; private set
; }
   
public double Fps { get; set
; }
   
public double Brightness { get; set
; }
   
public ImageSource CurrentFrame { get; set
; }
}

The code snippet above is the ViewModel. It represents the view of a 2D camera inspection unit. For the simplicity it exposes only relevant properties to the relevant view.

Some of the properties are straight forward, such as Fps, and other represent collection: Defects.

 

Let’s take a look on the view itself.

<UserControl d:DesignHeight="300" d:DesignWidth="300">    
   
<Grid>
        <Image Source="{Binding CurrentFrame}" />
        <ListBox ItemsSource="{Binding Defects}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="RenderTransform">
                        <Setter.Value>
                            <TranslateTransform X="{Binding Zone.X}"
                                               Y="{Binding Zone.Y}" />
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock>
                            <Run Text="Defect #" />
                            <Run Text="{Binding Id}" />
                        </TextBlock>
                        <Rectangle Width="{Binding Zone.Width}"
                               Height="{Binding Zone.Height}" />
                    </StackPanel>
                   
               
</DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBlock Text="Brightness" />
        <Slider Value="{Binding Brightness}" />
        <TextBlock Text="{Binding Fps}" />
    </Grid>   
</UserControl>

For clarity, I’ve removed brushes and layout properties.

Now there should be piece of code gluing the View with the ViewModel by setting the View.DataContext with the ViewModel instance. There are several techniques for doing that: Hardcoded, MEF, Service Locator, Others.

No matter what’s your favorite technique, both Expression Blend and Visual Studio keen to locate the data-context object inside the XAML file at design time. Failed to do so results in null data-context and no data is presented at design time.

imageimage

How to instantiate the view-model so it will be rendered by the view at design time?

Well, we can create an instance of the view-model directly within the view’s XAML, but I’m sure that you agree that this is not a best practice and it is not possible in several circumstances such when the view-model has no default constructor and/or have to be instantiated using a DI container or should be created and populated by the business layer itself.

There are several ways solving this problem. In this post I’ll cover my favorite one using Design Time only DataContext.

Working with Blend 3,4 and VS2010, there is a magical designer property called d:DataContext. This property is DataContext for design time only. Using this property you can keep setting the view’s regular DataContext property with a view-model as usual and then set the view’s magical d:DataContext designer property with a different design-time view-model instance created in XAML. Opening the view at design-time using Blend or Visual Studio, the d:DataContext will take place. Running the application the regular DataContext will be used.

This solution is almost perfect. Now all we have to do is to generate a design-time view-model from within the XAML file.

Now let’s face it, no one, including the XAML designer likes generating fake data with bare hands, and here comes Blend to the rescue. There is a cool feature in Blend for generating data within XAML. There are several options of course but one of them is best fit our need. We’ll generate data based on our view-model class.

To do so, open the project with Blend, open the relevant view, go to data tab on the right, select project, click on the first small icon on the right and choose “Create Sample Data from class…”. Pick the relevant view-model and then click ok.

imageimageimage

At this moment blend created a new folder called SampleData and a new XAML file.

imageimageimage

If you open the XAML file created you’ll find the following generated markup:

<Blendability_Modules_ViewModels:Camera2dInspectionViewModel
   xmlns:Blendability_Modules_ViewModels="clr-namespace:Blendability.Modules.ViewModels"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   Brightness="638.53"
   Fps="990.7">
<Blendability_Modules_ViewModels:Camera2dInspectionViewModel.Defects>
<Blendability_Modules_ViewModels:DefectViewModel Id="63">
<Blendability_Modules_ViewModels:DefectViewModel.Zone>
<Rect Height="315.65" Width="181.51" X="468.12" Y="991.84">
<Rect.Location>
<Point X="988.41" Y="166.96"/>
</Rect.Location>
<Rect.Size>
<Size Height="422.96" Width="258.6"/>
</Rect.Size>
</Rect>
</Blendability_Modules_ViewModels:DefectViewModel.Zone>
</Blendability_Modules_ViewModels:DefectViewModel>
	</Blendability_Modules_ViewModels:Camera2dInspectionViewModel.Defects>
</
Blendability_Modules_ViewModels:Camera2dInspectionViewModel>

Amazingly Blend generated deign-time instance from our view-model and populated it with auto-generated random data, no matter if the view-model has no default constructor, or in our case the Fps property is read-only. Also it dug inside the view-model, generated all public properties including collections. I had to cut some XAML for clarity.

Of course, I had to change the data and add missing properties such as CurrentFrame, as nothing is perfect in this world especially software.

Here is the final result of the data I changed:

<vm:Camera2dInspectionViewModel
xmlns:vm="clr-namespace:Blendability.Modules.ViewModels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Brightness="0.4"
Fps="29.7">
    <vm:Camera2dInspectionViewModel.CurrentFrame>
        <BitmapImage UriSource="/SampleData/Penguins.jpg" />
    </vm:Camera2dInspectionViewModel.CurrentFrame>
    <vm:Camera2dInspectionViewModel.Defects>
        <vm:DefectViewModel Id="1">
            <vm:DefectViewModel.Zone>
                <Rect Height="40" Width="40" X="68" Y="15" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
        <vm:DefectViewModel Id="2">
            <vm:DefectViewModel.Zone>
                <Rect Height="25" Width="25" X="180" Y="50" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
        <vm:DefectViewModel Id="3">
            <vm:DefectViewModel.Zone>
                <Rect Height="50" Width="50" X="50" Y="90" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
    </vm:Camera2dInspectionViewModel.Defects
>
</
vm:Camera2dInspectionViewModel>

Now that we have design-time only view-model, lets connect it with the view’s d:DataContext by simply opening the view and drag the sample view-model from the Blend’s data tab into the view’s root element located in the Objects and Timeline tab on the left.

image

Hurrah… now you can be happy, watching the view rendering the sample view-model at design-time and you can now edit the style of your view’s controls, edit the data template of the Defects collection and see the big picture of your view without running the whole application again and again.

Please feel free to download the demo code from here.

In the next post I’ll reveal my little research on Blendability of Prism Modules. This one is really a neat feature, so stay tuned.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

10 comments

  1. BobFebruary 7, 2011 ב 06:21

    What if you want to show the same design time data when you actually run the app? Sometimes getting real data for an app can take a while and to have the same dummy data load when the app is run would be useful. Is there any easy way to have the design time data also show up when you run the app?

    Reply
  2. Tomer ShamamFebruary 7, 2011 ב 09:31

    There is an option. Just set DataContext with the sample data instead of d:DataContext, and don’t forget to replace to the real one later. This can also be done from Blend, there is a checkbox for that when adding sample data.

    Reply
  3. Michał ZalewskiMarch 13, 2011 ב 15:16

    Wow! It’s great!

    Reply
  4. Wes March 27, 2011 ב 23:53

    This is a handy tip, especially as I too don’t want to code all that sample data by hand.
    @ my job, we haven’t moved to visual studio 2010 yet, and I note that the “Sample Data from Class” feature is only available in Blend 4.

    Is there a way to do the same thing in Blend 3?

    I just spent 2 hrs undoing all project changes Blend 4 made when it upgraded all my projects in my solution to 2010. I was hoping Blend 4 was ok w/ VS2008 proj files, but no sir, it did not like it.

    Reply
  5. Tomer ShamamMarch 29, 2011 ב 15:00

    Hi Wes,

    Blend 3 supports sample data a bit differently, but it does. Either way, it doesn’t support .NET 4, only 3.5.
    As for the vs2010 file format, you can change the target framework to 3.5, then force blend to load vs2010 solution.

    Reply
  6. AsigurariAugust 24, 2011 ב 17:58

    It seems simple enough when you have a tutorial like this in front of your eyes.

    Reply
  7. MusaabJune 11, 2012 ב 17:36

    Thats great, the sample data just appeared as it should, but it is not touchable, I have two TextBlock controls inside a DataTemplate of a ListBox, they don’t apppear in the Objects and TimeLine Panel and I can’t touch them with the mouse to modify them, any idea ?

    Reply
  8. BenMarch 15, 2013 ב 12:50

    Great saved loads of effort with some of my view, it doesn’t work with ICollectionView though which is a shame.

    Reply
  9. Pingback: WPF: DataTemplates and Design-Time data | Sergey Brunov