65.9K
CodeProject 正在变化。 阅读更多。
Home

Blendability 第一部分 – 设计时 ViewModel

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (5投票s)

2011 年 1 月 17 日

CPOL

4分钟阅读

viewsIcon

18282

Blendability 第一部分 – 设计时 ViewModel

如果您还没有将“混合性”(Blendability)这个词添加到您的 XAML 术语库中,我相信这篇文章一定会激发您这样做。

“混合性”这个术语描述了数据模型或视图模型在设计时(无论是通过 Expression Blend 还是 Visual Studio Designer)的可视化或设计能力。

构建 Silverlight 或 WPF 应用程序时,大家都喜欢使用 MVVM 模式。这种模式极大地解耦了视图与其逻辑和域模型,从而能够轻松地进行单元测试并提供极大的灵活性。在大多数情况下,除了视图构造函数中的 `InitializeComponent`(对于 `UserControl`)之外,XAML 后面不应该有任何代码。MVVM 模式的这一重要方面应该能够帮助 XAML 设计师使用任何喜欢的工具(例如 Blend)来更改视图,而不会受到视图逻辑的干扰。

让我们来看一个小应用程序,它使用 MVVM 模式来显示一个简单的视图。

public class Camera2dInspectionViewModel : NotificationObject
{
    public ObservableCollection<DefectViewModel> Defects { get; private set; }
    public double Fps { get; set; }
    public double Brightness { get; set; }
    public ImageSource CurrentFrame { get; set; }
}

上面的代码片段是 `ViewModel`。它代表了一个二维相机检查单元的视图。为了简单起见,它只向相关视图公开相关的属性。

其中一些属性是直观的,例如 `Fps`,而另一些则代表集合:`Defects`。

让我们来看看视图本身。

<UserControl d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Image Source="{Binding CurrentFrame}" />
        <ListBox ItemsSource="{Binding Defects}">
            <ListBox.ItemContainerStyle>
                <Style TargetType="{x:Type ListBoxItem}">
                    <Setter Property="RenderTransform">
                        <Setter.Value>
                            <TranslateTransform X="{Binding Zone.X}"
                                                Y="{Binding Zone.Y}" />
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock>
                            <Run Text="Defect #" />
                            <Run Text="{Binding Id}" />
                        </TextBlock>
                        <Rectangle Width="{Binding Zone.Width}"
                                Height="{Binding Zone.Height}" />
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
        <TextBlock Text="Brightness" />
        <Slider Value="{Binding Brightness}" />
        <TextBlock Text="{Binding Fps}" />
    </Grid>
</UserControl>

为了清晰起见,我已删除笔刷和布局属性。

现在,应该有一段代码通过将 `View.DataContext` 设置为 `ViewModel` 实例来连接 `View` 和 `ViewModel`。有几种技术可以做到这一点:硬编码、MEF、服务定位器、其他。

无论您喜欢哪种技术,Expression Blend 和 Visual Studio 都热衷于在设计时在 XAML 文件中查找数据上下文对象。如果失败,数据上下文将为 `null`,并且在设计时将不显示任何数据。

imageimage

如何实例化视图模型,以便视图在设计时呈现它?

嗯,我们可以直接在视图的 XAML 中创建视图模型的实例,但我相信您同意这不是最佳实践,并且在某些情况下(例如视图模型没有默认构造函数、需要使用 DI 容器实例化或需要由业务层自身创建和填充)是不可能的。

有几种方法可以解决这个问题。在这篇文章中,我将介绍我最喜欢的一种方法,即使用仅用于设计时的 `DataContext`。

在使用 Blend 3、4 和 VS2010 时,有一个神奇的设计器属性称为 `d:DataContext`。此属性仅用于设计时 `DataContext`。使用此属性,您可以像往常一样继续将视图的常规 `DataContext` 属性设置为视图模型,然后将视图的、神奇的 `d:DataContext` 设计器属性设置为在 XAML 中创建的不同设计时视图模型实例。使用 Blend 或 Visual Studio 打开设计时的视图时,将应用 `d:DataContext`。运行应用程序时,将使用常规的 `DataContext`。

这个解决方案几乎是完美的。现在我们所要做的就是从 XAML 文件中生成一个仅用于设计时的视图模型。

现在,我们必须承认,没有人(包括 XAML 设计师)喜欢手动生成假数据,而 Blend 在这里大有帮助。Blend 有一个很酷的功能,可以在 XAML 中生成数据。当然有几种选择,但其中一种最适合我们的需求。我们将根据我们的视图模型类生成数据。

要做到这一点,请使用 Blend 打开项目,打开相关的视图,转到右侧的数据选项卡,选择项目,单击右侧第一个小图标,然后选择“从类创建示例数据…”选择相关的视图模型,然后单击确定。

imageimageimage

此时,Blend 创建了一个名为 *SampleData* 的新文件夹和一个新的 XAML 文件。

imageimageimage

如果您打开创建的 XAML 文件,您会发现以下生成的标记:

<Blendability_Modules_ViewModels:Camera2dInspectionViewModel
    xmlns:Blendability_Modules_ViewModels="clr-namespace:Blendability.Modules.ViewModels"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    Brightness="638.53"
    Fps="990.7">
	<Blendability_Modules_ViewModels:Camera2dInspectionViewModel.Defects>
		<Blendability_Modules_ViewModels:DefectViewModel Id="63">
			<Blendability_Modules_ViewModels:DefectViewModel.Zone>
				<Rect Height="315.65" Width="181.51"
					X="468.12" Y="991.84">
					<Rect.Location>
						<Point X="988.41" Y="166.96"/>
					</Rect.Location>
					<Rect.Size>
						<Size Height="422.96"
						Width="258.6"/>
					</Rect.Size>
				</Rect>
			</Blendability_Modules_ViewModels:DefectViewModel.Zone>
		</Blendability_Modules_ViewModels:DefectViewModel>
	</Blendability_Modules_ViewModels:Camera2dInspectionViewModel.Defects>
</Blendability_Modules_ViewModels:Camera2dInspectionViewModel>

令人惊讶的是,Blend 从我们的视图模型生成了设计时实例,并用自动生成的随机数据填充了它,即使视图模型没有默认构造函数,或者在我们的例子中 `Fps` 属性是只读的。它还深入研究了视图模型,生成了所有 `public` 属性,包括集合。为了清晰起见,我不得不删除一些 XAML。

当然,我不得不更改数据并添加缺失的属性,例如 `CurrentFrame`,因为在这个世界上没有什么是不完美的,尤其是软件。

这是我更改数据的最终结果:

<vm:Camera2dInspectionViewModel
	xmlns:vm="clr-namespace:Blendability.Modules.ViewModels"
	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
	Brightness="0.4"
	Fps="29.7">
    <vm:Camera2dInspectionViewModel.CurrentFrame>
        <BitmapImage UriSource="/SampleData/Penguins.jpg" />
    </vm:Camera2dInspectionViewModel.CurrentFrame>
    <vm:Camera2dInspectionViewModel.Defects>
        <vm:DefectViewModel Id="1">
            <vm:DefectViewModel.Zone>
                <Rect Height="40" Width="40" X="68" Y="15" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
        <vm:DefectViewModel Id="2">
            <vm:DefectViewModel.Zone>
                <Rect Height="25" Width="25" X="180" Y="50" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
        <vm:DefectViewModel Id="3">
            <vm:DefectViewModel.Zone>
                <Rect Height="50" Width="50" X="50" Y="90" />
            </vm:DefectViewModel.Zone>
        </vm:DefectViewModel>
    </vm:Camera2dInspectionViewModel.Defects>
</vm:Camera2dInspectionViewModel>

现在我们有了仅用于设计时的视图模型,只需打开视图并将 Blend 数据选项卡中的示例视图模型拖放到左侧“对象和时间线”选项卡中视图的根元素上,即可将其与视图的 `d:DataContext` 连接。

image

太棒了……现在您可以高兴地在设计时查看视图渲染示例视图模型,并且您可以编辑视图控件的样式,编辑 `Defects` 集合的数据模板,并看到视图的全局图景,而无需一次又一次地运行整个应用程序。

请随时从此处下载演示代码。

在下一篇文章中,我将揭示我关于 Prism 模块混合性的小研究。这确实是一个很棒的功能,敬请期待。

© . All rights reserved.