WPF中有哪些可用于虚构设计时数据的方法?

101

我使用VS2010的XAML编辑器而没有使用Expression Blend。除了这个问题,我越来越需要设计时间数据绑定。对于简单情况,FallbackValue属性非常好用(如文本框和文本块等)。但是,特别是在处理ItemsControl等组件时,需要在设计器中显示样本数据,以便您可以调整和调试控件和数据模板而无需运行可执行文件。

我知道ObjectDataProvider允许绑定到类型,因此可以提供可视化的设计时间数据,但是需要一些操作才能使实际的运行时数据绑定而不会浪费资源,同时加载设计时间虚假数据和运行时绑定。

实际上,我想要的是,在我的ItemsControl中,例如“John”、“Paul”、“George”和“Ringo”出现为可样式化项目,并在应用程序运行时显示真实数据。

我也知道Blend允许使用一些定义设计期绑定数据的花哨属性,在运行时条件下被WPF忽略。

所以我的问题是:

1. 我如何利用Visual Studio XAML设计器中的集合和非平凡数据的设计时间绑定,然后平稳过渡到运行时绑定?

2. 别人是如何解决设计时间与运行时数据问题的?在我这种情况下,很难同时使用相同的数据(就像数据库查询一样)。

3. 除了Expression Blend之外,是否有其他可用于集成数据的XAML设计工具?(我知道有一些替代品,但我特别想要一个可以使用并查看绑定样本数据等的工具。)

8个回答

121

使用VS2010,您可以使用设计时属性(适用于SL和WPF)。我通常都有一个模拟数据源,所以只需要:

  • 添加命名空间声明

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  • 将模拟数据上下文添加到窗口/控件资源中

  • <UserControl.Resources>
      <ViewModels:MockXViewModel x:Key="DesignViewModel"/>
    </UserControl.Resources>
    
  • 设置设计时数据上下文

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

    运行良好。


    2
    如果您在使用 d:DataContext 时遇到问题,可以在这个问题中找到一些帮助:https://dev59.com/hGsy5IYBdhLWcg3w0RM5 - Martin Liversage
    28
    这个例子是否会在发布版本中加载MockXViewModel实例到你的资源中呢?这不是一个问题吗? - jpierson
    12
    请注意:如果没有以下内容,VS2012编译器将无法编译xaml文件:xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"mc:Ignorable="d"。请将其添加到文件中,并确保语法正确。 - Orion Edwards
    56
    jpierson 是正确的。我更喜欢使用 <Grid d:DataContext="{d:DesignInstance Type=ViewModels:MockXViewModel, IsDesignTimeCreatable=True}" ...> 这种方式。这样,模拟的 viewmodel 仅在设计器中创建,而不是在运行应用程序时创建。请注意,此方法要求您的模拟视图模型具有无参数的构造函数。但在上面给出的答案示例中也是如此。 - René
    2
    @René你的方法更好。请将其添加为答案,我会投票支持它。 - dss539
    显示剩余3条评论

    18
    作为 Goran 的答案和 Rene 的评论的综合。
    • 添加命名空间声明:xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

    • 从代码中引用您的设计时数据上下文。
      <Grid d:DataContext="{d:DesignInstance Type=ViewModels:MockXViewModel, IsDesignTimeCreatable=True}" ...


    1
    我很想将其标记为新答案,但也许我们可以补充其他细节。 - el2iot2
    1
    这需要更多的关注,或者它需要被纳入到被接受的答案中。这是一个更好的解决方案。 - Lauraducky
    为什么这样更好?起初我认为被接受的答案会在运行时不必要地产生模拟模型,但我测试了一下,实际上并没有。如果没有使用,资源就不会被创建。 - Paul
    @Paul 这实际上是个人偏好问题,但这个答案将整个设计时间数据上下文保持在一个声明中,而不是分散在两个位置。这样更容易进行更改。 - John Stritenberger
    1
    @JohnStritenberger 这不仅仅是个人偏好问题,被接受的答案会不必要地将资源一直加载到内存中,而不仅仅是为了设计师。 - StayOnTarget
    在查看了被接受的答案后,您是正确的。他们正在将一个类的实例添加到“UserControl.Resources”中,事实上,它会永久存在。@UuDdLrLrSs - John Stritenberger

    6
    我使用这种方法来生成.NET 4.5和Visual Studio 2013的设计时数据。
    我只有一个ViewModel。 ViewModel有一个属性“IsInDesignMode”,用于判断是否处于设计模式下(请参阅类“ViewModelBase”)。 然后,您可以在视图模型构造函数中设置设计时数据(例如填充项控件)。
    此外,我不会在视图模型构造函数中加载真实数据,因为这可能会导致运行时问题,但是为设计时设置数据不应成为问题。
    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
        }       
    }
    

    4
    使用Visual Studio 2017,我一直在尝试遵循所有指南和问题,例如这个问题。但我仍然面临一个<ItemsControl>,它根本不执行我在DesignFooViewModel构造函数中的代码,该构造函数继承自FooViewModel。我通过此"方便的" MSDN指南来确认了“未执行”部分(提示:使用MessageBox进行调试)。虽然这与原始问题没有直接关系,但我希望它能为其他人节省很多时间。
    结果证明我没有做错任何事情。问题在于我的应用程序需要构建为x64。由于Visual Studio在2018年仍然是32位进程并且显然无法为设计器部分旋转64位主机进程,因此它无法使用我的x64类。真正糟糕的是,在我能想到的任何日志中都找不到错误。
    因此,如果您因为在设计时视图模型中看到虚假数据而遇到此问题(例如:<TextBlock Text="{Binding Name}"/>显示Name,无论您将属性设置为什么),原因可能是您的x64构建。如果由于依赖关系而无法将构建配置更改为anycpu或x86,请考虑创建一个全面任意CPU且没有依赖关系(或任何依赖关系)的新项目。这样,您最终将把代码的大部分或全部初始化部分从“WPF App”项目中拆分到一个“C#类库”项目中。
    对于我正在处理的代码库,我认为这将强制实现关注点分离,尽管会有一些代码重复,但这可能是净积极的事情。

    4

    感谢您提醒我。我喜欢DesignAndRunTimeDataContext的概念。 - el2iot2
    1
    Karl Shifflett有一篇更新的关于Visual Studio 2010的文章:WPF和Silverlight Designer中的示例数据 - totorocat
    2
    链接内容的要点应该真正地编辑到答案中,特别是第一个链接现在已经失效了。 - Lauraducky

    4
    也许Visual Studio 2010和Expression Blend 4的新设计时功能是您的选择。
    它的工作原理在WPF应用程序框架(WAF)的示例应用程序中展示。请下载.NET4版本。

    谢谢你的链接。有没有特定的代码文件或结构,我应该看一下来了解这种方法?(简要概述会很好) - el2iot2
    请查看BookLibrary.Presentation项目。在这个项目中,您会找到“DesignData”文件夹,该文件夹被“Views”文件夹中的用户控件使用。 - jbe
    1
    刚才看了一下这个。对于任何感兴趣的人来说,示例数据视图模型在XAML中声明,并通过d:DataContext = "{d:DesignData Source = ../DesignData/SampleLendToViewModel.xaml}"进行引用。 - RichardOD

    4

    与最高评分答案类似,但在我看来更好:您可以创建一个静态属性来返回设计数据的实例,并直接从XAML引用它,如下所示:

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

    这样可以避免使用 UserControl.Resources。你的静态属性可以作为工厂,允许你构建非常规的数据类型,例如如果你没有默认构造函数,你可以在这里调用一个工厂或容器来注入适当的依赖项。

    0

    我喜欢jbe的建议,特别是看看WAF框架示例应用程序中的实现方式 - 他们在DesignData文件夹中使用单独的模拟/样本视图模型,然后在XAML中添加以下行:

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

    (其中 dd 指向 .DesignData 命名空间,MockHomeViewModel 存储在其中)

    这个方法很简单(我喜欢!),你可以继承真正的 VM 并提供虚假数据。它将代码分开以防止您的真正 VM 受到任何仅在设计时存在的代码的污染。当然,在使用 IOC 等的大型项目中,情况可能会有所不同,但对于小型项目来说,它很有效。

    但正如 joonas 指出的那样,在 VS2017 中似乎无法与 x64 构建一起使用,并且在 VS2019 (我正在使用 V2019 16.6 社区版)中仍然存在这种情况。一开始让它工作并不困难,但是在进行更改后(通常情况下是多次更改!)它突然停止工作会让人感到头疼。

    对于任何尝试此方法的人,我建议创建一个新的简单 WPF 项目(例如一个视图、一个视图模型和一个模拟的 VM),并进行实验;让它工作然后再破坏它。我发现有时候,没有解决方案清理和重建可以修复它,唯一有效的方法是关闭 VS 然后重新启动,突然间我的设计时数据回来了!


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接