Click here to Skip to main content
15,884,388 members
Articles / Desktop Programming / WPF
Tip/Trick

Using Design-time Databinding While Developing a WPF User Control

Rate me:
Please Sign up or sign in to vote.
4.40/5 (4 votes)
26 Feb 2015CPOL4 min read 77K   729   12   3
A trick that allows populating a user control with sample data while you are designing it in the Visual Studio designer

Introduction

When one designs WPF UI elements in Microsoft Visual Studio or Blend, it is very beneficial to see them populated with sample data. Using sample data ensures proper layout and allows one to see data-specific effects (e.g., effects of very long stings in bound properties) without running the application.

For example, if one designs a simple progress report user control that has a progress bar with an overlaid message and a progress value, he might not discover problems with the design until he runs the application.

Image 1

Figure 1. View of a progress report control in the Visual Studio designer

On the other hand, as soon as the control is data bound at design time, one can easily see that the current design has problems:

Image 2

Figure 2. View of the same progress report control in the Visual Studio designer when it is design-time data bound to sample data

There are a fair amount of articles on the net that describe how to use the design-time data binding while working with WPF/Silverlight Windows and Pages. However, those methods do not directly apply when one designs a user control. This tip describes a trick to make design-time data binding working even for user controls.

Background

Visual Studio 2010 introduced support for design-time data binding in its Designer view. To use it, all one needs is to include into a Window, a Page, or a User Control XAML file a couple of additional namespaces...

XML
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"

...and a number of new design-time attributes become available for use. The following articles describe design-time data binding in detail:

The most important of the design-time attiributes is d:DataContext. It can be set for any FrameworkElement and specifies the design-time DataContext for a control and its children. The designer then uses the context to populate the control binding in the Design view and to display sample data in the designer. For example:

XML
 xmlns:dvm="clr-namespace:Dima.Controls.DesignViewModel"
 d:DataContext ="{d:DesignInstance {x:Type dvm:ProgressReportSample1}, IsDesignTimeCreatable=True}"

This works well for the content of WPF/Silverlight Windows and Pages. However, user controls in many cases ignore the DataContext and instead expose dependency properties that their host needs to bind to the data. This makes direct use of the d:DataContext attribute in user controls impossible and one needs to resolve to a trick.

User Control Design

As an example, let's consider the progress report user control shown in figures 1 and 2. It defines the Percentage, Message and CancelCommand dependency properties:

C#
public partial class ProgressReportControl : UserControl
{
    public ProgressReportControl()
    {
        InitializeComponent();
        this.root.DataContext = this;
    }

    public static readonly DependencyProperty PercentageProperty = 
        DependencyProperty.Register("Percentage", typeof(int), typeof(ProgressReportControl));
    public int Percentage
    {
        get { return (int)GetValue(PercentageProperty); }
        set { SetValue(PercentageProperty, value); }
    }

    public static readonly DependencyProperty MessageProperty =
        DependencyProperty.Register("Message", typeof(string), typeof(ProgressReportControl));
    public string Message
    {
        get { return (string)GetValue(MessageProperty); }
        set { SetValue(MessageProperty, value); }
    }

    public static readonly DependencyProperty CancelCommandProperty = 
        DependencyProperty.Register("CancelCommand", typeof(ICommand), typeof(ProgressReportControl));
    public ICommand CancelCommand
    {
        get { return (ICommand)GetValue(CancelCommandProperty); }
        set { SetValue(CancelCommandProperty, value); }
    }
}

and binds its elements to those properties:

XML
<DockPanel x:Name="root" Margin="4">
    <Button Content="Cancel" Command="{Binding CancelCommand, Mode=OneWay}" 
            Width="60" Margin="4,0,4,0" 
            DockPanel.Dock="Right" VerticalAlignment="Center" />
    <Grid Margin="4,0,4,0" Height="32">
        <ProgressBar Value="{Binding Percentage, 
        Mode=OneWay}" Minimum="0" Maximum="100" />
        <TextBlock Text="{Binding ElementName=progressBar, Path=Value, StringFormat={}{0:0}%}" 
                   HorizontalAlignment="Center" VerticalAlignment="Center" />
        <TextBlock Text="{Binding Message, Mode=OneWay}" 
                   Margin="4,0,4,0" VerticalAlignment="Center" DockPanel.Dock="Top" />
    </Grid>
</DockPanel>

At runtime, when the control is loaded, we need to ensure that its elements are bound to the dependency properties and not to the arbitrary DataContext that the control inherits from its host. So, in the control’s constructor, we set DataContext of its child root element to the control itself.

C#
this.root.DataContext = this;

Any window that hosts the progress report control will need to bind the control properties to the data. The DataContext that it passes to the control is ignored within the control.

XML
<Controls:ProgressReportControl Grid.Row="1" 
HorizontalAlignment="Stretch" VerticalAlignment="Top"
          Message="{Binding Report.Message}" Percentage="{Binding Report.Percentage}" 
          CancelCommand="{Binding ComputationCancel}" />

At first glance, this completely eliminates the possibility to use the design-time data passed as d:DataContext.

Trick Description

However, we should recall that when a user control is designed in the Design view, the designer does not execute its constructor (though it will execute constructors of all its child elements). Thus, if we create a design-time view model which shape matches control's dependency properties...

C#
public class ProgressReportSample1
{
    public int Percentage { get; private set; }
    public string Message { get; private set; }
    public ICommand CancelCommand { get; private set; }
    public ProgressReportSample1()
    {
        this.Percentage = 60;
        this.Message = "Computation progress";
        this.CancelCommand = null;
    }
}

...and pass it as design-time sample data via d:DataContext to the designed user control, the control child elements will see it:

XML
<UserControl x:Class="Dima.Controls.ProgressReportControl"
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     mc:Ignorable="d" d:DesignHeight="80" d:DesignWidth="300"
             
     xmlns:dvm="clr-namespace:Dima.Controls.DesignViewModel"
     d:DataContext ="{d:DesignInstance {x:Type dvm:ProgressReportSample1}, 
     IsDesignTimeCreatable=True}">

Due to the matching shape, the designer will successfully bind the user control elements to the properties of the design-time view model and we will get the control view shown in figure 2.

At the same time, when we design the window hosting our user control, the window constructor again will not be executed, but the control constructor will. Thus, when the host window is designed, the control will ignore the window's design-time view model passed to it as DataContext and will properly bind to the control’s dependency properties:

Image 3

Figure 3. Visual Studio designer view of a window hosting the progress report control. The control is populated with design-time data via its properties

Conclusion

The described above usage of design-time data binding is just a trick, not an all-encompassing solution, but it should work for most of the user controls. The attached UseControlDesignTimeDataBinding.zip file contains the full source code for the tip.

History

  • 2/27/2015 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionHow to fill if I have a Path data? Pin
Rupam Bhaduri Pro3-Aug-15 0:48
Rupam Bhaduri Pro3-Aug-15 0:48 
QuestionNice article - one question Pin
David Steverson2-Mar-15 6:20
professionalDavid Steverson2-Mar-15 6:20 
AnswerRe: Nice article - one question Pin
Dmitriy Repin2-Mar-15 11:22
Dmitriy Repin2-Mar-15 11:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.