【问题标题】:How do I make WPF data bindings refactor safe?如何使 WPF 数据绑定重构安全?
【发布时间】:2009-09-11 20:35:03
【问题描述】:

所以我正在完成我的第一个 WPF 项目,我喜欢我目前所看到的。学习曲线比我预期的要多,但是 WPF 还是很酷的。但是,我在数据绑定概念上有点挣扎。我的一个具体问题是如何使我的数据绑定声明重构安全?考虑这个例子。

public class MyDataObject
{
  public string FooProperty { get; set; }
}

void Bind() 
{
  var gridView = myListView.View as GridView;
  gridView.Columns.Clear();
  gridView.Columns.Add(
    new GridViewColumn() 
      { 
        Header = "FooHeader", 
        DisplayMember = new Binding("FooProperty")
      }
    );
  List<MyDataObject> source = GetData();
  myListView.ItemsSource = source;
}

那么,如果我将数据对象上的 FooProperty 重命名为其他名称会怎样?数据绑定将无效,并且我不会收到编译错误,因为绑定仅通过文本声明。有没有办法让绑定更安全地重构?

【问题讨论】:

    标签: c# .net wpf data-binding


    【解决方案1】:

    您可以使用 lambda 表达式来表示属性名称,而不是直接使用名称:

        protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
        {
            if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess)
            {
                PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo;
                if (prop != null)
                {
                    return prop.Name;
                }
            }
            throw new ArgumentException("expression", "Not a property expression");
        }
    

    你会这样使用它:

    ...
    DisplayMember = new Binding(GetPropertyName((MyDataObject o) => o.FooProperty))
    ...
    

    好的,有点冗长...如果您想要更短的内容,您还可以创建一个辅助方法:

    public Binding CreateBinding<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
    {
        return new Binding(GetPropertyName(expression))
    }
    
    ...
    DisplayMember = CreateBinding((MyDataObject o) => o.FooProperty)
    ...
    

    这样,如果您重命名属性,重构应该可以正常工作(当然在 XAML 中除外......)

    【讨论】:

    • 非常聪明。是的,xaml 仍然是一个问题,但至少在我眼前的场景中,无论如何我都必须动态构建列,因此目前无需担心。谢谢!
    【解决方案2】:

    重构依赖于工具支持识别代码(C#、XAML、配置等)中的特定符号何时代表被重命名的标识符。

    在您给出的示例中,如果不了解 GridView 的内部工作原理以及 WPF 和其他框架中的所有其他类型的扩展知识,字符串文字 "FooProperty" 不能 100% 解释为属于 MyDataObject

    但是,在 DataTemplate 中,有可能 99% 确定:

    <DataTemplate DataType="{x:Type local:MyDataObject}">
        <TextBlock Text="{Binding Path=FooProperty}" />
    </DataTemplate>
    

    我使用(并且发誓)一个名为 ReSharper(又名 R#)的 IDE 插件,它对这类事情非常聪明。如果您重命名FooProperty,R# 会自动为您重命名该属性。

    在您的示例中,如果您要重命名属性,R# 仍然可以使用。它会在文字(您的情况)和 c​​mets 中查找字符串的所有实例(如果您已注释掉一些代码并且稍后可能会取消注释,这将非常有用)。系统会为您提供一个树形视图,在上下文中显示每个文字,您可以在继续之前选中/取消选中各个用法/文件/文件夹/项目。

    如果您的预算允许,请购买 R#。如果您的预算不允许,请下载试用版,在试用期结束时,您的预算会找到空间。请务必打印一份快捷键的副本以改善您的学习体验。

    【讨论】:

      【解决方案3】:

      您可以使用反射来确定属性的名称。当然,如果每个类有多个绑定,这是有问题的,因此可能另外使用自定义属性(也可以通过反射获得)来获取有关命名属性应该绑定到的正确绑定字段的“提示”。

      这很可能最终只是将不可重构的魔术字符串重新定位到应用程序的不同部分,但我不能说我已经尝试过并且可以正常工作。

      【讨论】:

      • 重构发生在设计时,而反射发生在运行时。 (实际上在语言 'io' 中,重构可以在运行时发生,但这是另一回事!)
      • 当然,但据我了解,重构不会在绑定中捕获 FooProperty 的“魔术字符串”,因此属性会更改而绑定不会;通过在运行时通过反射获得正确的字符串,您总是在查看最新的世界,而不是依赖于确保您更改了所有常量。
      • @Mikeb - 这取决于您使用的重构工具。
      猜你喜欢
      • 2010-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-15
      • 2011-10-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多