SuperMe SuperMe - 2 months ago 30
C# Question

Binding to design data in WPF

I have a WPF window containing a ListBox. The ItemsSource is bound to a property of a view model.

<Window x:Class="SimpleWpfApp.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding MainWindowViewModel, Source={StaticResource Locator}}">
<DockPanel>
<ListBox ItemsSource="{Binding SomeThings}" />
</DockPanel>
</Window>


The property of the view model is an observable collection of a custom interface; ISomeInterface. The interface is very simple and is implemented by SomeClass which additionally overrides ToString.

public class MainWindowViewModel
{
public ObservableCollection<ISomeInterface> SomeThings
{
get
{
var list = new List<ISomeInterface>
{
new SomeClass {Value = "initialised"},
new SomeClass {Value = "in"},
new SomeClass {Value = "code"}
};

return new ObservableCollection<ISomeInterface>(list);
}
}
}

public interface ISomeInterface
{
string Value { get; }
}

public class SomeClass : ISomeInterface
{
public string Value { get; set; }

public override string ToString() => Value;
}


When I view the window in Visual Studio 2015 or Blend all is as expected. ToString is called and the ListBox populated.

Blend screenshot

I have created XAML design data which I want to use when in design mode. I have added the design data in a directory called SampleData. I add a design datacontext statement to the window XAML immediately below the first DataContext.

d:DataContext="{d:DesignData Source=/SampleData/Data.xaml}"


This doesn't work. Visual Studio and Blend report 'File or project item not found' regardless of what I use for source path. I have tried /SampleData/Data.xaml, SampleData/Data.xaml, ../SampleData/Data.xaml, ./../SampleData/Data.xaml

Visual Studio and Blend only find Data.xaml if I move it out of the SampleData directory and into the project root. Then I am able to reference it using source path /Data.xaml or Data.xaml. If I use Data.xaml without prefixing / then Visual Studio and Blend report that the file cannot be found.. but find it anyway.

My first question is .. Can I use sample data in a sub-directory? And if so how?

Having successfully referenced Data.xaml in the project root, my window is not calling the overridden ToString so I'm getting a list of class name displayed. The list has the same number of items as the design data so it appears it is using the design data.

My second question is .. Why is the overridden ToString not being called here when it is if the objects are instantiated from code?

I'm aware I can achieve the desired result by specifying an item template.

<ListBox ItemsSource="{Binding SomeThings}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>


Full source is available for the example application on github

https://github.com/DangerousDarlow/WpfDesignData

UPDATE

Thanks to jstreet for answering. I changed the file properties for data.xaml in the sub directory and am now able to use this as design data. I thought I'd tried this before but I must be mistaken.

I'm still not seeing ToString being called. I tried changing the view model property to
List<object>
and also
List<ISomeInterface>
but both resulted in called to object.ToString; deduced by the display of the class name. I'll probably stop looking at this point as I'm not going to be using ToString anyway, I'll bind to the properties I want to display. It would be good to explain the difference in behaviour though.

I'm using Visual Studio 2015 community edition.

Answer

Here's some working sample code. You may want to refer to This article - MSDN.

In particular, note how to set properties for your Data.xaml file (Dictionary1.xaml, in my case) in your VS project:

enter image description here

Also note how to create your root object, SomeThings (SomeClasses in my case):

For collections, the root object can be an ArrayList or a custom type that derives from a collection or generic collection...

XAML:

<Window x:Class="WpfApplication277.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication277"
    d:DataContext="{d:DesignData Source=/SampleData/Dictionary1.xaml}"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <ListView ItemsSource="{Binding}"></ListView>
</Grid>

Dictionary1.xaml:

Right-click SampleData folder in your VS project, and select Add\New Item\WPF\Resource Dictionary, replace its contents with your design data. This should make sure your design data can be located in a sub-folder.

<m:SomeClasses xmlns:m="clr-namespace:WpfApplication277">
<m:SomeClass Value="design data 1">
</m:SomeClass>
<m:SomeClass Value="design data 2">
</m:SomeClass>
<m:SomeClass Value="design data 3">
</m:SomeClass>

SomeClasses: List<SomeClass> did NOT work !

public class SomeClasses : List<Object>
{
    public SomeClasses() { }
}

SomeClass:

public class SomeClass : ISomeInterface
{
    public string Value { get; set; }

    public override string ToString() => string.Format("ToString() : {0}",Value);
}

Note that ToString() is definitely being called:

enter image description here

Comments