在 WPF 中可以使用哪些方法来虚拟设计时数据?

我的工作没有表达式混合,只是在 vs2010中使用 XAML 编辑器。撇开这一点不谈,我越来越看到对设计时数据绑定的需求。对于简单的情况,FallbackValue属性工作得非常好(Textbox 和 TextBlock 等)。但是,特别是在处理 ItemsControl之类的时候,您确实需要在设计器中显示示例数据,这样您就可以调整和调整控件和数据模板,而不必运行可执行文件。

我知道 ObjectDataProvider允许绑定到一个类型,因此可以为可视化提供设计时数据,但是还有一些工作要做,通过加载设计时、虚拟数据和运行时绑定来允许实际的运行时数据绑定,而不会浪费资源。

我真正想要的是能够在 XAML 设计器中将“ John”、“ Paul”、“ George”和“ Ringo”显示为 ItemsControl中的可样式化项目,但是在应用程序运行时会显示真实的数据。

我还知道 Blend 允许一些奇特的属性,这些属性定义了 WPF 在运行时条件下有效忽略的设计时绑定数据。

所以我的问题是:

1.如何在可视化工作室 XAML 设计器中利用集合和非平凡数据的设计时绑定,然后平稳地切换到运行时绑定?

2.其他人如何解决这个设计时与运行时数据的问题?在我的例子中,我不能很容易地为两者使用相同的数据(例如,一个数据库查询就可以做到这一点)。

3.他们的替代表达式混合,我可以用于数据集成的 XAML 设计? (我知道有一些替代方案,但是我特别想要一些可以使用的东西,并且可以查看绑定的示例数据,等等?)

39545 次浏览

Karl Shifflett describes an approach that ought to work equally well for VS2008 and VS2010:

Viewing Design Time Data in Visual Studio 2008 Cider Designer in WPF and Silverlight Projects

Laurent Bugnion has a similar approach that focuses on Expression Blend. It might work for VS2010, but I haven't confirmed this yet.

Simulating data in design mode in Microsoft Expression Blend

Maybe the new design-time features of Visual Studio 2010 and Expression Blend 4 are an option for you.

How it works is shown in the BookLibrary sample application of the WPF Application Framework (WAF). Please download the .NET4 version.

Using VS2010 you can use Design-Time attributes (works for both SL and WPF). I usually have a mock data-source anyway so it's just a matter of:

  • Adding the namespace declaration

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    
  • Adding the mock data context to window/control resources

    <UserControl.Resources>
    <ViewModels:MockXViewModel x:Key="DesignViewModel"/>
    </UserControl.Resources>
    
  • Setting design-time data context

    <Grid d:DataContext="{Binding Source={StaticResource DesignViewModel}}" ...
    

Works well enough.

I use this approach for generating design time data with .NET 4.5 and Visual Studio 2013.

I have just one ViewModel. The view model has a property IsInDesignMode which tells whether design mode is active or not (see class ViewModelBase). Then you can set up your design time data (like filling an items control) in the view models constructor.

Besides, I would not load real data in the view models constructor, this may lead to issues at runtime, but setting up data for design time should not be a problem.

public abstract class ViewModelBase
{
public bool IsInDesignMode
{
get
{
return DesignerProperties.GetIsInDesignMode(new DependencyObject());
}
}
}


public class ExampleViewModel : ViewModelBase
{
public ExampleViewModel()
{
if (IsInDesignMode == true)
{
LoadDesignTimeData();
}
}


private void LoadDesignTimeData()
{
// Load design time data here
}
}

Similar to the top rated answer, but better in my opinion: You can create a static property to return an instance of design data and reference it directly from XAML like so:

<d:UserControl.DataContext>
<Binding Source="{x:Static designTimeNamespace:DesignTimeViewModels.MyViewModel}" />
</d:UserControl.DataContext>

This avoids the need to use UserControl.Resources. Your static property can function as a factory allowing you to construct non-trivial data types - for example if you do not have a default ctor, you can call a factory or container here to inject in appropriate dependencies.

As an amalgam of Goran's accepted answer and Rene's excellent comment.

  • Add the namespace declaration. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

  • Reference your design time data context from code.
    <Grid d:DataContext="{d:DesignInstance Type=ViewModels:MockXViewModel, IsDesignTimeCreatable=True}" ...

Using Visual Studio 2017 I have been trying to follow all of the guides and questions such as this and I was still facing a <ItemsControl> which simply did not execute the code I had inside the constructor of a DesignFooViewModel which inherits from FooViewModel. I confirmed the "did not execute" part following this "handy" MSDN guide (spoiler: MessageBox debugging). While this is not directly related to the original question, I hope it will save others a lot of time.

Turns out I was doing nothing wrong. The issue was that my application needs to be built for x64. As the Visual Studio is still in 2018 a 32-bit process and apparently cannot spin a 64-bit host process for the designer part it cannot use my x64 classes. The really bad thing is that there are no errors to be found in any log I could think of.

So if you stumble upon this question because you are seeing bogus data in with your design time view model (for example: <TextBlock Text="{Binding Name}"/> shows up Name no matter you set the property to) the cause is likely to be your x64 build. If you are unable to change your build configuration to anycpu or x86 because of dependencies, consider creating a new project which is fully anycpu and does not have the dependencies (or any dependencies). So you end up splitting most or all but the initialization parts of the code away from your "WPF App" project into a "C# class library" project.

For the codebase I am working on I think this will force healthy separation of concerns at the cost of some code duplication which is probably net positive thing.

I liked jbe's suggestion, specifically to look at how they do it in the WAF framework sample apps - they use separate mock/sample view models in a DesignData folder and then have a line like this in the XAML:

mc:Ignorable="d"
d:DataContext="{d:DesignInstance dd:MockHomeViewModel, IsDesignTimeCreatable=True}"

(where dd points to the .DesignData namespace where MockHomeViewModel lives)

It's nice and simple (which I like!) and you can inherit from the real VMs and just provide dummy data. It keeps things separate as you don't need to pollute your real VMs with any design time only code. I appreciate things might look quite different for a large project utilising IOCs etc but for small projects it works well.

But as joonas pointed out, it seems not to work with x64 builds in VS2017 and this still seems to be the case with VS2019 (I'm using V2019 16.6 Community edition). It's not fiddly to get working to start off with but can cause some head scratching when after making a change (or as is usually the case, several changes!) it suddenly stops working.

For anybody trying it, I would recommend creating a new simple WPF project (say one view, one view model, one mock vm) and play around with it; get it working and then break it. I found sometimes, no amount of solution cleans and rebuilds would fix it, the only thing that worked was closing VS down and restarting, and suddenly my design time data came back!