问题 绑定的静态验证


要么 ”你如何确保所有绑定保持正确?
(这有点冗长,但请耐心等待,我尽量做到尽可能短)

请考虑以下示例:

    <TextBox Name="tb" />
    <TextBlock Text="{Binding Text.TheProp, ElementName=tb}" />

在编译时完全知道绑定是不正确的(即解析器知道元素的类型 tb,因此,它知道它的类型 Text 财产,因此,它知道 TheProp 不存在)。
但是,此代码将编译并运行(尽管在调试输出中有绑定错误消息)。

在某些情况下,这种行为可能非常方便:无论我的数据类型是什么,只要它具有适当命名的属性,我就可以了。因此,我们得到了一种“声明性的鸭子打字”。

然而鸭打字并不总是好事。
具体来说,在使用MVVM模式时,我(大部分时间)都知道我所有ViewModel对象的确切类型。另一方面,模型随着时间的推移变得越来越复杂,这使我对未来的重构感到担忧:如果我决定重命名一些属性,或者,上帝禁止,将它们放在一个单独的聚合对象中会怎么样?然后我的所有绑定会发生什么?我是否必须手动耙所有XAML文件?即使没有重构 - 如果我只是打错了怎么办?

在XAML的其他地方已经解决了类似的问题。例如,如果您输入了错误的属性名称 Style/Setter/@Property,你得到一个编译时错误。
TemplateBinding 还提供此类验证。哪个非常方便。

所以,理想情况下,我希望看到这样的事情:

ProductViewModel.cs:

    public class ProductViewModel
    {
        public Name { get; set; }
        public Price { get; set; }
    }

ProductView.XAML:

    <UserControl x:Class="Shopping.View.ProductView"
                 x:DataContextType="vm:ProductViewModel"
                 xmlns:vm="clr-namespace:Shopping.ViewModel"
                 ... >
        <TextBox Text="{Binding Name}" />  <!-- OK -->
        <TextBox Text="{Binding Price}" /> <!-- OK -->
        <TextBox Text="{Binding ABC}" />   <!-- Compile time error: there is no property ABC in ProductViewModel -->
    </UserControl>

ShoppingCart.XAML:

    <UserControl x:Class="Shopping.View.ShoppingCartView"
                 x:DataContextType="vm:ShoppingCartViewModel"
                 xmlns:vm="clr-namespace:Shopping.ViewModel"
                 ... >
        <ItemsControl ItemsSource="{Binding Products}"
                      ItemType="vm:ProductViewModel" >  <!-- Static check happens here 
                                                             ShoppingCartViewModel.Products must 
                                                             implement IEnumerable<ProductViewModel> -->
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="vm:ProductViewModel">
                    <view:ProductView /> <!-- DataContext is known to be of correct type
                                              because of DataTemplate.DataType property -->
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </UserControl>

但让我们回到现实。实际上,所有梦想都不会在不久的将来发生。

但是,我确信我不是第一个遇到这个问题的人。
所以,最后,问题是: 你如何确保你的绑定是正确的?并且他们保持这种方式?


3919
2017-09-04 14:57


起源

UWP现在通过提供静态绑定 {x:Bind} 标记扩展。神秘的方式:) - Funk


答案:


您的Xaml静态分析如何作为构建后步骤执行?

作为.Net 4的一部分,微软发布了一款新版本 System.Xaml 库提供独立于WPF的强大Xaml解析和序列化支持。他们现在开始在它上面构建各种有趣的东西,其中一些可能会帮助你。

在里面 XamlToolkit例如,你会发现 XamlDOM 这使您可以轻松地对Xaml文件进行静态分析。更进一步,那就是 XxL的FxCop规则

最感兴趣的是Rob Relyea的 BindingFinder 它具有在Xaml中类型检查Bindings的明确目标。这要求你在Xaml中有类型提示,比如 数据类型 DataTemplate中的属性,或新的属性 d:DataContext属性 在您的视图上(Blend用于提供设计时数据)。然后它使用XamlDOM检查所有内容是否匹配。

更新:  Resharper 6 现在提供 用于数据绑定的intellisense如果您的财产路径错误,请发出警告。


8
2017-09-09 10:34



...... Aaaaaaa和200名塞缪尔杰克的名声!谢谢,先生,这几乎就是我想要的。我实际上正处于自己实现这样的事情的边缘,但有一个地方开始总是更好。 :-) - Fyodor Soikin
@Fyodor - 很高兴我能为您服务! - Samuel Jack
Visual Studio 2010和XAML需要开箱即用的“BindingFinder”功能! - thenonhacker


实际上,至少在使用MVVM模式时,我从未发现这是一个问题。视图模型仅存在于视图的支持中。我不会在不改变另一个的情况下改变一个。重构视图模型不会破坏视图中的绑定,因为重构视图模型本身是没有意义的。您只会在(并且因为)更改视图设计时重构视图模型。

我没有这个问题的另一个原因是我没有独立于Expression Blend开发视图模型。对于除了最简单的UI之外的所有UI,我使用某种依赖注入构建我的视图模型,以便我可以创建可在Expression Blend中使用的测试数据源。当我在Blend中创建绑定时,我立刻知道我是否做得对。

和一般的MVVM一样,这样做是一个难以置信的痛苦,直到你明白你在做什么以及为什么。 (这篇博文很长 作者:JonasFollesø对如何使用提供了很好的概述 Ninject 为了这个目的,虽然你可以使用其他框架的结尾。)我确信我还没有发现这个方法的问题 - 超出了我添加DI框架和表达式的问题混合到我需要理解的东西堆上来开发WPF应用程序。

帕布罗卡萨尔斯说,不断的实验让艺术家保持年轻。我不 感觉 年轻。


2
2017-09-05 00:11



好吧,似乎我们对MVVM的理解不同。当我理解它时,应该将ViewModel视为应用程序的实际UI,而View只是该UI的“皮肤”。举个例子,我可以引用这篇文章: msdn.microsoft.com/en-us/magazine/dd419663.aspx 这种方法实现了一些不错的功能。例如:一个人可以为同一个UI拥有多个“皮肤”。另一个例子:“皮肤”可以[相对]独立于UI本身设计,也可以由不同的人设计(通常称为“设计师”:-)。 [在下一条评论中继续] - Fyodor Soikin
又一个例子:如果有几个“皮肤”,其中一些可以由不同的人,不同的公司,甚至是客户独立设计!当然,单元测试呢?如果您将ViewModel视为View的“支持”,则在设计单元测试时应始终牢记View。即使从你的角度来看,它仍然不起作用:如果View是由另一个人(“设计师”)设计的,你将如何与ViewModel一起改变它? - Fyodor Soikin
最后,关于重构的必要性。我也要求在这方面有所不同。当系统变得过于复杂时,经常使用重构,因此需要将其分解为更简单的系统,或者以不同的方式重新安排其组件。一个简单的例子:假设一个屏幕应显示某些产品的某些信息。说,名称和价格。所以我的ViewModel具有这些属性,并且视图绑定到它们。然后有一个要求:添加“制造商”字段。只是一个简单的字符串OK完成。现在我的ViewModel有另一个名为“Manufacturer”的属性。[见下一个comnt] - Fyodor Soikin
一天后,又出现了另一项要求:添加制造商地址。好的,现在很明显,下一个要求可能是“添加制造商徽标”或其他东西。现在,为制造商创建另一个数据结构并让产品参考它可能是有意义的。根据其他情况(如ViewModel的复杂性),我可能还认为为制造商创建单独的ViewModel是有意义的,并且也让ProductViewModel引用它。那就是你的重构,就在这里。现在我需要去寻找所有相关的XAML绑定。 - Fyodor Soikin
看,我永远不会允许你描述的任何事情。我的视图模型与我的观点紧密相关。期。如果我想要有相同数据的三个相似视图,我不会创建一个视图模型,它是三个视图的超集,我是视图模型的子类。我为制造商创建单独的视图模型的唯一原因是我还创建了一个单独的视图。如果您认为视图模型过于复杂而没有将其扩展到视图,那么您将引入一种比您尝试修复的更复杂的复杂性类别。 - Robert Rossney