Tag Archives: wpf

WPF: DataTemplates and Design-Time data

It seems to be useful to have a possibility to edit DataTemplate rendered with Design-Time data.

First, let’s consider the following ViewModel interface:

internal interface IBookViewModel
{
    string Author { get; set; }

    string Title { get; set; }
}

Let’s also create a separate XAML-file for ResourceDictionary where the DataTemplate will be located:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:App.ViewModel"
                    mc:Ignorable="d">
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

Second, let’s edit the DataTemplate using Blend for Visual Studio 2012 (see Expression Studio 4.0: Create or modify a template):

  1. Open the solution in Blend.
  2. Locate the XAML-file containing the ResourceDictionary using the “Resources” panel.
  3. Locate the DataTemplate in the list of resources by its Key.
  4. Click “Edit Resource” button.

Blend will show the message “BookViewModelTemplate.xaml cannot be edited in the Design view.”

Third, to display Design-Time data attribute d:DataContext must be used to provide the Design-Time data for the designer.
Also, there is d:DesignInstance markup extension to simplify the Design-Time instance creation.
Blend uses the following Design-Time-related XAML-namespaces (see DesignData MVVM support in Blend, VS2010 and WPF/Silverlight):

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

To provide the Design-Time Data instance, let’s create a class that will implement the ViewModel interface and contain the appropriate data:

internal sealed class DesignBookViewModel : IBookViewModel
{
    public DesignBookViewModel()
    {
        Title = "Test title";
        Author = "Test author";
    }

    public string Author { get; set; }

    public string Title { get; set; }
}

Finally, after experiments it seems the d:DataContext attributes does not work for DataTemplate element directly, but the immediate child element of the DataTemplate.

The following result examples are identical. Both work just fine!

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:App.ViewModel"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    mc:Ignorable="d">
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical"
                    d:DataContext="{d:DesignInstance Type=viewModel:DesignBookViewModel, IsDesignTimeCreatable=True}">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:BlendabilityDataTemplate.ViewModel"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    mc:Ignorable="d">
    <viewModel:DesignBookViewModel x:Key="DesignBookViewModel" />
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical"
                    d:DataContext="{Binding Source={StaticResource DesignBookViewModel}}">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

Enjoy!

P.S. There is also another approach (out of the scope of this post) – create a DataSource using Blend. It implies adding additional XAML-file that defines ViewModel instance (i.e. initialization).
The disadvantage of this approach: the ViewModel can be just an interface or concrete class, but without public setters – no way to set property values using XAML.
More details here:

  1. DesignData MVVM support in Blend, VS2010 and WPF/Silverlight.
  2. Blendability Part I – Design time ViewModel.

References:

  1. Mastering XAML (Design-Time attributes) by Adam Nathan.
  2. WPF 4.5 Unleashed, Adam Nathan.

StackOverflow references:

  1. What approaches are available to dummy design-time data in WPF?.
  2. Setting design time DataContext on a Window is giving a compiler error?.
  3. Making a DataTemplate blendable.
  4. Design time data for datatemplate in xaml.

WPF: PasswordBox control example

It was necessary to implement password box with “show password” check box which shows the original password.

This article WPF PasswordBox Control is a good place to start. I’ve used the PasswordHelper class from the article.

The PasswordHelper is attached to the password box by calling the PasswordHelper.Attach property. The attached property PasswordHelper.Password provides a bindable copy of the original password property of the PasswordBox control.

Here is the code for the class:

public static class PasswordHelper
{
    public static readonly DependencyProperty PasswordProperty =
        DependencyProperty.RegisterAttached("Password",
        typeof(string), typeof(PasswordHelper),
        new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));

    public static readonly DependencyProperty AttachProperty =
        DependencyProperty.RegisterAttached("Attach",
        typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, Attach));

    private static readonly DependencyProperty IsUpdatingProperty =
       DependencyProperty.RegisterAttached("IsUpdating", typeof(bool), 
       typeof(PasswordHelper));


    public static void SetAttach(DependencyObject dp, bool value)
    {
        dp.SetValue(AttachProperty, value);
    }

    public static bool GetAttach(DependencyObject dp)
    {
        return (bool)dp.GetValue(AttachProperty);
    }

    public static string GetPassword(DependencyObject dp)
    {
        return (string)dp.GetValue(PasswordProperty);
    }

    public static void SetPassword(DependencyObject dp, string value)
    {
        dp.SetValue(PasswordProperty, value);
    }

    private static bool GetIsUpdating(DependencyObject dp)
    {
        return (bool)dp.GetValue(IsUpdatingProperty);
    }

    private static void SetIsUpdating(DependencyObject dp, bool value)
    {
        dp.SetValue(IsUpdatingProperty, value);
    }

    private static void OnPasswordPropertyChanged(DependencyObject sender,
        DependencyPropertyChangedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;
        passwordBox.PasswordChanged -= PasswordChanged;

        if (!(bool)GetIsUpdating(passwordBox))
        {
            passwordBox.Password = (string)e.NewValue;
        }
        passwordBox.PasswordChanged += PasswordChanged;
    }

    private static void Attach(DependencyObject sender,
        DependencyPropertyChangedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;

        if (passwordBox == null)
            return;

        if ((bool)e.OldValue)
        {
            passwordBox.PasswordChanged -= PasswordChanged;
        }

        if ((bool)e.NewValue)
        {
            passwordBox.PasswordChanged += PasswordChanged;
        }
    }

    private static void PasswordChanged(object sender, RoutedEventArgs e)
    {
        PasswordBox passwordBox = sender as PasswordBox;
        SetIsUpdating(passwordBox, true);
        SetPassword(passwordBox, passwordBox.Password);
        SetIsUpdating(passwordBox, false);
    }
}

Here is the main window XAML. Don’t forget to reference the namespace of PasswordHelper class (in my case: xmlns:w="clr-namespace:TestWpfApp").

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto" />
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="Auto" />
    </Grid.ColumnDefinitions>

    <TextBlock Text="Password:" />

    <PasswordBox x:Name="passwordBox"
             Grid.Column="1"
                 w:PasswordHelper.Attach="True"
                 w:PasswordHelper.Password="{Binding Text, ElementName=textBox, Mode=TwoWay}">
        <PasswordBox.Style>
            <Style TargetType="{x:Type PasswordBox}">
                <Setter Property="Visibility" Value="Visible" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsChecked, ElementName=showPassCheckBox}" Value="True">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </PasswordBox.Style>
    </PasswordBox>

    <TextBox x:Name="textBox"
         Grid.Column="1">
        <TextBox.Style>
            <Style TargetType="{x:Type TextBox}">
                <Setter Property="Visibility" Value="Visible" />
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsChecked, ElementName=showPassCheckBox}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed" />
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </TextBox.Style>
    </TextBox>

    <CheckBox x:Name="showPassCheckBox" Grid.Column="2" Content="Show password" />
</Grid>

Result:

Window with PasswordBox and TextBox controls

WPF: Specify your DataContext type

Using WPF with MVVM approach it is hard to keep in memory all types of the ViewModels you’re working with for databinding.
There is a quite useful feature which allows you to specify type of DataContext explicitly for each View (UI element).

Assume you have BookViewModel class with Author, Title properties. Also, you have a StackPanel in your XAML to display information about some book.

  1. Add the XML-namespaces to your root View: xmlns:d="http://schemas.microsoft.com/expression/blend/2008", xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006".
  2. Add the attribute mc:Ignorable="d" to your root View.
  3. Add the following StackPanel:
<StackPanel Orientation="Vertical" d:DataContext="{d:DesignInstance vm:BookViewModel}">
    <TextBlock Text="{Binding Path=Author, Mode=OneWay}" />
    <TextBlock Text="{Binding Path=Title, Mode=OneWay}" />
</StackPanel>

Also, JetBrains ReSharper will show you the warning if DataContext type is unknown:

Cannot resolve symbol ‘…’ due to unknown DataContext.

But when the DataContext type is specified and you try using the property which does not exist in the ViewModel, ReSharper will warn you:

Cannot resolve symbol ‘…’.

This makes data binding implementation less error-prone! 😀
Additional information: Walkthrough: Using a DesignInstance to Bind to Data in the Designer.

ObservableKeyedCollection class!

I am writing a file management application at the moment and I have the following ViewModel in the application:

public class DirectoryViewModel : ViewModelBase
{
    public SomeBindableDictionary<string, DirectoryViewModel> Directories
    {
        get;
        private set;
    }

    /// <summary>
    /// Unique name of directory.
    /// </summary>
    public string Name
    {
        get;
        private set;
    }

    // Skipped...
}

So, when a directory is removed from a physical file system, the Directories dictionary is used to perform remove the corresponding directory ViewModel by the directory name (directory name is used as an index).

Have found two collection classes that solve the problem.
* ObservableDictionary. The class represents keys and values as separate entities. It isn’t so convenient to bind to Key and Value property in HierarchicalDataTemplate. Furthermore, Items and Values collections properties do not implement INotifyCollectionChanged interface.
* ObservableKeyedCollection. The class uses the key which is a part (!) of the values you’d like to store in the collection. The collection does not separate key and value. You need just to bind to the collection as you did it before with ObservableCollection<T> class. So, this option suits the requirements very well.

Directories = new ObservableKeyedCollection<string, DirectoryViewModel>(directory => directory.Name);
Directories.AddRange(childDirectories);

The piece of code is quite self-descriptive: it creates an observable keyed collection for directories, directory name is used as a key.

To remove the directory by it’s name you just need to call:

Directories.Remove("Some directory");

The collection change will be automatically reflected on the UI.

Download

P.S. After downloading please rename .doc to .zip.