Wednesday, 26 November 2008

ObjectDataProvider

To make UI design easier in Blend we want to provide dummy data at design time. Using an ObjectDataProvider with an ObjectType attribute achieves this (provided the bound datasource can provide dummy data).

<ObjectDataProvider x:Key="DesignTimeDataProvider" d:IsDataSource="True" ObjectType="{x:Type Dummy_Application:PeopleDataProvider}"/>
However it is not possible to replace the ObjectType with an ObjectInstance at runtime.
We got round that problem by using two ObjectDataProviders:
<ObjectDataProvider x:Key="DesignTimeDataProvider" d:IsDataSource="True" ObjectType="{x:Type Dummy_Application:PeopleDataProvider}"/>
<ObjectDataProvider x:Key="InnerDataProvider" d:IsDataSource="true" ObjectInstance="{StaticResource DesignTimeDataProvider}"/>
<ListBox ItemsSource="{Binding Path=DataProvider.PeopleList, Source={StaticResource InnerDataProvider}}"/>
Then in the code behind we replace the ObjectInstance with our freshly minted data source:
ObjectDataProvider odpWindow = (ObjectDataProvider)this.FindResource("InnerDataProvider");
odpWindow.ObjectInstance= newPeopleDataProvider;
The problem with this approach is that at runtime a datasource is created (as part of InitializeComponent) that is then immediately discarded.

To resolve this I created my own ObjectDataProvider which nulls the ObjectType at runtime. This is enough to allow the ODP to accept a change to the ObjectInstance without complaint.
public class MyObjectDataProvider : ObjectDataProvider
{
bool isInDesignMode = (bool)DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue;
public new Type ObjectType
{
get { return base.ObjectType; }
set
{
if (!isInDesignMode) value = null;
base.ObjectType = value;
}
}
}

public partial class PersonEditView : Window
{
public PersonEditView()
{
InitializeComponent();
((ObjectDataProvider)this.FindResource("DataModel")).ObjectInstance = model; }
}
The xaml then becomes simpler and cleaner:
<local:MyObjectDataProvider x:Key="DataModel" d:IsDataSource="True" ObjectType="{x:Type local:PersonEditModel}" />
<TextBlock Text="{Binding Source={StaticResource DataModel}, Path=APerson.Surname}"/>

No comments: