如何使数据库类型安全并支持重构?

时间:2023-01-11 16:03:46

When I wish to bind a control to a property of my object, I have to provide the name of the property as a string. This is not very good because:

当我希望将控件绑定到对象的属性时,我必须将属性的名称作为字符串提供。这不是很好,因为:

  1. If the property is removed or renamed, I don’t get a compiler warning.
  2. 如果删除或重命名属性,则不会得到编译器警告。
  3. If a rename the property with a refactoring tool, it is likely the data binding will not be updated.
  4. 如果使用重构工具重命名属性,很可能不会更新数据绑定。
  5. I don’t get an error until runtime if the type of the property is wrong, e.g. binding an integer to a date chooser.
  6. 如果属性的类型错误,例如将一个整数绑定到一个日期选择器,则在运行时之前不会出现错误。

Is there a design-pattern that gets round this, but still has the ease of use of data-binding?

是否有一种设计模式可以绕过这个问题,但仍然能够轻松地使用数据绑定?

(This is a problem in WinForm, Asp.net and WPF and most likely lots of other systems)

(这是WinForm、Asp.net和WPF中的一个问题,很可能还有很多其他系统)

I have now found "workarounds for nameof() operator in C#: typesafe databinding" that also has a good starting point for a solution.

我现在已经找到了“c#: typesafe databinding中的nameof()操作符的工作区”,这也为解决方案提供了一个很好的起点。

If you are willing to use a post processor after compiling your code, notifypropertyweaver is well worth looking at.

如果您愿意在编译代码之后使用post处理器,notifypropertyweaver非常值得一看。


Anyone knows of a good solution for WPF when the bindings are done in XML rather then C#?

当绑定是用XML而不是c#进行时,谁知道WPF的一个好解决方案?

7 个解决方案

#1


51  

Thanks to Oliver for getting me started I now have a solution that both supports refactoring and is type safe. It also let me implement INotifyPropertyChanged so it copes with properties being renamed.

由于奥利弗的帮助,我现在有了一个既支持重构又类型安全的解决方案。它还允许我实现INotifyPropertyChanged,以便它能够复制被重命名的属性。

It’s usage looks like:

这是使用的样子:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

The person class shows how to implemented INotifyPropertyChanged in a type safe way (or see this answer for a other rather nice way of implementing INotifyPropertyChanged, ActiveSharp - Automatic INotifyPropertyChanged also looks good ):

person类显示了如何以一种安全的方式实现INotifyPropertyChanged(或者看到另一种很好的实现INotifyPropertyChanged的方法,ActiveSharp - Automatic INotifyPropertyChanged也看起来不错):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }

   // etc

   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
}

The WinForms binding helper class has the meat in it that makes it all work:

WinForms binding helper类中包含了使其工作的内容:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

This makes use of a lot of the new stuff in C# 3.5 and shows just what is possible. Now if only we had hygienic macros lisp programmer may stop calling us second class citizens)

这使得在c# 3.5中使用了很多新内容,并显示了可能的内容。现在,如果我们有卫生的宏lisp程序员就可以停止称呼我们为二等公民了)

#2


27  

The nameof operator was implemented in C# 6.0 with .NET 4.6 and VS2015 in July 2015. The following is still valid for C# < 6.0

To avoid strings which contain property names, I've written a simple class using expression trees to return the name of the member:

为了避免包含属性名称的字符串,我编写了一个简单的类,使用表达式树返回成员的名称:

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

You can use this class as follows. Even though you can use it only in code (so not in XAML), it is quite helpful (at least for me), but your code is still not typesafe. You could extend the method Name with a second type argument which defines the return value of the function, which would constrain the type of the property.

您可以使用这个类,如下所示。即使您只能在代码中使用它(所以在XAML中不能),它仍然非常有用(至少对我是这样),但是您的代码仍然不是类型设置。您可以使用第二个类型参数扩展方法名称,该参数定义函数的返回值,这将限制属性的类型。

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

Until now I haven't found anything which solves the databinding typesafety issue.

到目前为止,我还没有找到任何解决数据库类型问题的方法。

Best Regards

致以最亲切的问候

#3


23  

The Framework 4.5 provides us with the CallerMemberNameAttribute, which makes passing the property name as a string unnecessary:

框架4.5为我们提供了CallerMemberNameAttribute,这使得将属性名作为字符串传递是不必要的:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute.

如果您正在使用安装了KB2468871的Framework 4.0,您可以通过nuget安装Microsoft BCL兼容包,它也提供了这个属性。

#4


5  

This blog article raises some good questions about the performance of this approach. You could improve upon those shortcomings by converting the expression to a string as part of some kind of static initialization.

这篇博客文章对这种方法的性能提出了一些很好的问题。您可以通过将表达式转换为字符串作为某种静态初始化的一部分来改进这些缺点。

The actual mechanics might be a little unsightly, but it would still be type-safe, and approximately equal performance to the raw INotifyPropertyChanged.

实际的机制可能有点难看,但是它仍然是类型安全的,并且与原始INotifyPropertyChanged的性能大致相同。

Something kind of like this:

有点像这样:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}

#5


3  

One way to get feedback if your bindings are broken, is to create a DataTemplate and declare its DataType to be the type of the ViewModel that it binds to e.g. if you have a PersonView and a PersonViewModel you would do the following:

如果绑定被破坏,获得反馈的一种方法是创建一个DataTemplate,并将其数据类型声明为它绑定到的ViewModel的类型,例如,如果您有一个PersonView和PersonViewModel,您将执行以下操作:

  1. Declare a DataTemplate with DataType = PersonViewModel and a key (e.g. PersonTemplate)

    使用DataType = PersonViewModel和一个键(例如PersonTemplate)声明一个DataTemplate

  2. Cut all PersonView xaml and paste it into the data template (which ideally can just be at the top of the PersonView.

    剪切所有PersonView xaml并将其粘贴到数据模板中(理想情况下,该模板只能位于PersonView的顶部)。

3a. Create a ContentControl and set the ContentTemplate = PersonTemplate and bind its Content to the PersonViewModel.

3 a。创建一个ContentControl并设置ContentTemplate = PersonTemplate并将其内容绑定到PersonViewModel。

3b. Another option is to not give a key to the DataTemplate and not set the ContentTemplate of the ContentControl. In this case WPF will figure out what DataTemplate to use, since it knows what type of object you are binding to. It will search up the tree and find your DataTemplate and since it matches the type of the binding, it will automatically apply it as the ContentTemplate.

3 b。另一个选项是不给DataTemplate一个键,也不设置ContentControl的ContentTemplate。在这种情况下,WPF将确定使用哪个DataTemplate,因为它知道要绑定到的对象的类型。它将搜索树并找到您的DataTemplate,由于它匹配绑定的类型,它将自动将其应用为ContentTemplate。

You end up with essentially the same view as before, but since you mapped the DataTemplate to an underlying DataType, tools like Resharper can give you feedback (via Color identifiers - Resharper-Options-Settings-Color Identifiers) as to wether your bindings are broken or not.

您最终得到的视图与以前基本相同,但是由于您将DataTemplate映射到底层数据类型,Resharper这样的工具可以给您反馈(通过颜色标识符- Resharper- optionssettingscolor标识符),以判断绑定是否被破坏。

You still won't get compiler warnings, but can visually check for broken bindings, which is better than having to check back and forth between your view and viewmodel.

您仍然不会得到编译器警告,但是可以直观地检查损坏的绑定,这比必须在视图和viewmodel之间来回检查要好。

Another advantage of this additional information you give, is, that it can also be used in renaming refactorings. As far as I remember Resharper is able to automatically rename bindings on typed DataTemplates when the underlying ViewModel's property name is changed and vice versa.

您提供的这个附加信息的另一个优点是,它还可以用于重命名重构。据我所知,Resharper能够在底层视图模型的属性名称被更改时自动重命名类型化数据板块上的绑定,反之亦然。

#6


3  

1.If the property is removed or renamed, I don’t get a compiler warning.

1。如果删除或重命名属性,则不会得到编译器警告。

2.If a rename the property with a refactoring tool, it is likely the data binding will not be updated.

2。如果使用重构工具重命名属性,很可能不会更新数据绑定。

3.I don’t get an error until runtime if the type of the property is wrong, e.g. binding an integer to a date chooser.

3所示。如果属性的类型错误,例如将一个整数绑定到一个日期选择器,则在运行时之前不会出现错误。

Yes, Ian, that are exactly the problems with name-string driven data binding. You asked for a design-pattern. I designed the Type-Safe View Model (TVM) pattern that is a concretion of the View Model part of the Model-View-ViewModel (MVVM) pattern. It is based on a type-safe binding, similar to your own answer. I've just posted a solution for WPF:

是的,Ian,这正是名称字符串驱动数据绑定的问题。您要求设计模式。我设计了类型安全视图模型(TVM)模式,它是模型-视图-视图模型(MVVM)模式的视图模型部分的具体体现。它基于类型安全绑定,类似于您自己的答案。我刚刚发布了一个WPF的解决方案:

http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

#7


1  

x:bind (also called "compiled data bindings") for XAML (universal app) in windows 10 and windows phone 10 may solve this problem, see https://channel9.msdn.com/Events/Build/2015/3-635

XAML(通用应用)在windows 10和windows phone 10中的绑定(也称为“编译数据绑定”)可以解决这个问题,参见https://channel9.msdn.com/events/build5/3-635

I can't find the on line docs for it, but have not put much effort in, as it is something I will not be using for some time. However this answer should be a useful pointer to other people.

我找不到它的在线文档,但是没有花太多精力,因为它是我一段时间不会使用的东西。然而,这个答案应该是对其他人有用的指示。

#1


51  

Thanks to Oliver for getting me started I now have a solution that both supports refactoring and is type safe. It also let me implement INotifyPropertyChanged so it copes with properties being renamed.

由于奥利弗的帮助,我现在有了一个既支持重构又类型安全的解决方案。它还允许我实现INotifyPropertyChanged,以便它能够复制被重命名的属性。

It’s usage looks like:

这是使用的样子:

checkBoxCanEdit.Bind(c => c.Checked, person, p => p.UserCanEdit);
textBoxName.BindEnabled(person, p => p.UserCanEdit);
checkBoxEmployed.BindEnabled(person, p => p.UserCanEdit);
trackBarAge.BindEnabled(person, p => p.UserCanEdit);

textBoxName.Bind(c => c.Text, person, d => d.Name);
checkBoxEmployed.Bind(c => c.Checked, person, d => d.Employed);
trackBarAge.Bind(c => c.Value, person, d => d.Age);

labelName.BindLabelText(person, p => p.Name);
labelEmployed.BindLabelText(person, p => p.Employed);
labelAge.BindLabelText(person, p => p.Age);

The person class shows how to implemented INotifyPropertyChanged in a type safe way (or see this answer for a other rather nice way of implementing INotifyPropertyChanged, ActiveSharp - Automatic INotifyPropertyChanged also looks good ):

person类显示了如何以一种安全的方式实现INotifyPropertyChanged(或者看到另一种很好的实现INotifyPropertyChanged的方法,ActiveSharp - Automatic INotifyPropertyChanged也看起来不错):

public class Person : INotifyPropertyChanged
{
   private bool _employed;
   public bool Employed
   {
      get { return _employed; }
      set
      {
         _employed = value;
         OnPropertyChanged(() => c.Employed);
      }
   }

   // etc

   private void OnPropertyChanged(Expression<Func<object>> property)
   {
      if (PropertyChanged != null)
      {
         PropertyChanged(this, 
             new PropertyChangedEventArgs(BindingHelper.Name(property)));
      }
   }

   public event PropertyChangedEventHandler PropertyChanged;
}

The WinForms binding helper class has the meat in it that makes it all work:

WinForms binding helper类中包含了使其工作的内容:

namespace TypeSafeBinding
{
    public static class BindingHelper
    {
        private static string GetMemberName(Expression expression)
        {
            // The nameof operator was implemented in C# 6.0 with .NET 4.6
            // and VS2015 in July 2015. 
            // The following is still valid for C# < 6.0

            switch (expression.NodeType)
            {
                case ExpressionType.MemberAccess:
                    var memberExpression = (MemberExpression) expression;
                    var supername = GetMemberName(memberExpression.Expression);
                    if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;
                    return String.Concat(supername, '.', memberExpression.Member.Name);
                case ExpressionType.Call:
                    var callExpression = (MethodCallExpression) expression;
                    return callExpression.Method.Name;
                case ExpressionType.Convert:
                    var unaryExpression = (UnaryExpression) expression;
                    return GetMemberName(unaryExpression.Operand);
                case ExpressionType.Parameter:
                case ExpressionType.Constant: //Change
                    return String.Empty;
                default:
                    throw new ArgumentException("The expression is not a member access or method call expression");
            }
        }

        public static string Name<T, T2>(Expression<Func<T, T2>> expression)
        {
            return GetMemberName(expression.Body);
        }

        //NEW
        public static string Name<T>(Expression<Func<T>> expression)
        {
           return GetMemberName(expression.Body);
        }

        public static void Bind<TC, TD, TP>(this TC control, Expression<Func<TC, TP>> controlProperty, TD dataSource, Expression<Func<TD, TP>> dataMember) where TC : Control
        {
            control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));
        }

        public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)
        {
            // as this is way one any type of property is ok
            control.DataBindings.Add("Text", dataObject, Name(dataMember));
        }

        public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)
        {       
           control.Bind(c => c.Enabled, dataObject, dataMember);
        }
    }
}

This makes use of a lot of the new stuff in C# 3.5 and shows just what is possible. Now if only we had hygienic macros lisp programmer may stop calling us second class citizens)

这使得在c# 3.5中使用了很多新内容,并显示了可能的内容。现在,如果我们有卫生的宏lisp程序员就可以停止称呼我们为二等公民了)

#2


27  

The nameof operator was implemented in C# 6.0 with .NET 4.6 and VS2015 in July 2015. The following is still valid for C# < 6.0

To avoid strings which contain property names, I've written a simple class using expression trees to return the name of the member:

为了避免包含属性名称的字符串,我编写了一个简单的类,使用表达式树返回成员的名称:

using System;
using System.Linq.Expressions;
using System.Reflection;

public static class Member
{
    private static string GetMemberName(Expression expression)
    {
        switch (expression.NodeType)
        {
            case ExpressionType.MemberAccess:
                var memberExpression = (MemberExpression) expression;
                var supername = GetMemberName(memberExpression.Expression);

                if (String.IsNullOrEmpty(supername))
                    return memberExpression.Member.Name;

                return String.Concat(supername, '.', memberExpression.Member.Name);

            case ExpressionType.Call:
                var callExpression = (MethodCallExpression) expression;
                return callExpression.Method.Name;

            case ExpressionType.Convert:
                var unaryExpression = (UnaryExpression) expression;
                return GetMemberName(unaryExpression.Operand);

            case ExpressionType.Parameter:
                return String.Empty;

            default:
                throw new ArgumentException("The expression is not a member access or method call expression");
        }
    }

    public static string Name<T>(Expression<Func<T, object>> expression)
    {
        return GetMemberName(expression.Body);
    }

    public static string Name<T>(Expression<Action<T>> expression)
    {
        return GetMemberName(expression.Body);
    }
}

You can use this class as follows. Even though you can use it only in code (so not in XAML), it is quite helpful (at least for me), but your code is still not typesafe. You could extend the method Name with a second type argument which defines the return value of the function, which would constrain the type of the property.

您可以使用这个类,如下所示。即使您只能在代码中使用它(所以在XAML中不能),它仍然非常有用(至少对我是这样),但是您的代码仍然不是类型设置。您可以使用第二个类型参数扩展方法名称,该参数定义函数的返回值,这将限制属性的类型。

var name = Member.Name<MyClass>(x => x.MyProperty); // name == "MyProperty"

Until now I haven't found anything which solves the databinding typesafety issue.

到目前为止,我还没有找到任何解决数据库类型问题的方法。

Best Regards

致以最亲切的问候

#3


23  

The Framework 4.5 provides us with the CallerMemberNameAttribute, which makes passing the property name as a string unnecessary:

框架4.5为我们提供了CallerMemberNameAttribute,这使得将属性名作为字符串传递是不必要的:

private string m_myProperty;
public string MyProperty
{
    get { return m_myProperty; }
    set
    {
        m_myProperty = value;
        OnPropertyChanged();
    }
}

private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed")
{
    // ... do stuff here ...
}

If you're working on Framework 4.0 with KB2468871 installed, you can install the Microsoft BCL Compatibility Pack via nuget, which also provides this attribute.

如果您正在使用安装了KB2468871的Framework 4.0,您可以通过nuget安装Microsoft BCL兼容包,它也提供了这个属性。

#4


5  

This blog article raises some good questions about the performance of this approach. You could improve upon those shortcomings by converting the expression to a string as part of some kind of static initialization.

这篇博客文章对这种方法的性能提出了一些很好的问题。您可以通过将表达式转换为字符串作为某种静态初始化的一部分来改进这些缺点。

The actual mechanics might be a little unsightly, but it would still be type-safe, and approximately equal performance to the raw INotifyPropertyChanged.

实际的机制可能有点难看,但是它仍然是类型安全的,并且与原始INotifyPropertyChanged的性能大致相同。

Something kind of like this:

有点像这样:

public class DummyViewModel : ViewModelBase
{
    private class DummyViewModelPropertyInfo
    {
        internal readonly string Dummy;

        internal DummyViewModelPropertyInfo(DummyViewModel model)
        {
            Dummy = BindingHelper.Name(() => model.Dummy);
        }
    }

    private static DummyViewModelPropertyInfo _propertyInfo;
    private DummyViewModelPropertyInfo PropertyInfo
    {
        get { return _propertyInfo ?? (_propertyInfo = new DummyViewModelPropertyInfo(this)); }
    }

    private string _dummyProperty;
    public string Dummy
    {
        get
        {
            return this._dummyProperty;
        }
        set
        {
            this._dummyProperty = value;
            OnPropertyChanged(PropertyInfo.Dummy);
        }
    }
}

#5


3  

One way to get feedback if your bindings are broken, is to create a DataTemplate and declare its DataType to be the type of the ViewModel that it binds to e.g. if you have a PersonView and a PersonViewModel you would do the following:

如果绑定被破坏,获得反馈的一种方法是创建一个DataTemplate,并将其数据类型声明为它绑定到的ViewModel的类型,例如,如果您有一个PersonView和PersonViewModel,您将执行以下操作:

  1. Declare a DataTemplate with DataType = PersonViewModel and a key (e.g. PersonTemplate)

    使用DataType = PersonViewModel和一个键(例如PersonTemplate)声明一个DataTemplate

  2. Cut all PersonView xaml and paste it into the data template (which ideally can just be at the top of the PersonView.

    剪切所有PersonView xaml并将其粘贴到数据模板中(理想情况下,该模板只能位于PersonView的顶部)。

3a. Create a ContentControl and set the ContentTemplate = PersonTemplate and bind its Content to the PersonViewModel.

3 a。创建一个ContentControl并设置ContentTemplate = PersonTemplate并将其内容绑定到PersonViewModel。

3b. Another option is to not give a key to the DataTemplate and not set the ContentTemplate of the ContentControl. In this case WPF will figure out what DataTemplate to use, since it knows what type of object you are binding to. It will search up the tree and find your DataTemplate and since it matches the type of the binding, it will automatically apply it as the ContentTemplate.

3 b。另一个选项是不给DataTemplate一个键,也不设置ContentControl的ContentTemplate。在这种情况下,WPF将确定使用哪个DataTemplate,因为它知道要绑定到的对象的类型。它将搜索树并找到您的DataTemplate,由于它匹配绑定的类型,它将自动将其应用为ContentTemplate。

You end up with essentially the same view as before, but since you mapped the DataTemplate to an underlying DataType, tools like Resharper can give you feedback (via Color identifiers - Resharper-Options-Settings-Color Identifiers) as to wether your bindings are broken or not.

您最终得到的视图与以前基本相同,但是由于您将DataTemplate映射到底层数据类型,Resharper这样的工具可以给您反馈(通过颜色标识符- Resharper- optionssettingscolor标识符),以判断绑定是否被破坏。

You still won't get compiler warnings, but can visually check for broken bindings, which is better than having to check back and forth between your view and viewmodel.

您仍然不会得到编译器警告,但是可以直观地检查损坏的绑定,这比必须在视图和viewmodel之间来回检查要好。

Another advantage of this additional information you give, is, that it can also be used in renaming refactorings. As far as I remember Resharper is able to automatically rename bindings on typed DataTemplates when the underlying ViewModel's property name is changed and vice versa.

您提供的这个附加信息的另一个优点是,它还可以用于重命名重构。据我所知,Resharper能够在底层视图模型的属性名称被更改时自动重命名类型化数据板块上的绑定,反之亦然。

#6


3  

1.If the property is removed or renamed, I don’t get a compiler warning.

1。如果删除或重命名属性,则不会得到编译器警告。

2.If a rename the property with a refactoring tool, it is likely the data binding will not be updated.

2。如果使用重构工具重命名属性,很可能不会更新数据绑定。

3.I don’t get an error until runtime if the type of the property is wrong, e.g. binding an integer to a date chooser.

3所示。如果属性的类型错误,例如将一个整数绑定到一个日期选择器,则在运行时之前不会出现错误。

Yes, Ian, that are exactly the problems with name-string driven data binding. You asked for a design-pattern. I designed the Type-Safe View Model (TVM) pattern that is a concretion of the View Model part of the Model-View-ViewModel (MVVM) pattern. It is based on a type-safe binding, similar to your own answer. I've just posted a solution for WPF:

是的,Ian,这正是名称字符串驱动数据绑定的问题。您要求设计模式。我设计了类型安全视图模型(TVM)模式,它是模型-视图-视图模型(MVVM)模式的视图模型部分的具体体现。它基于类型安全绑定,类似于您自己的答案。我刚刚发布了一个WPF的解决方案:

http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

http://www.codeproject.com/Articles/450688/Enhanced-MVVM-Design-w-Type-Safe-View-Models-TVM

#7


1  

x:bind (also called "compiled data bindings") for XAML (universal app) in windows 10 and windows phone 10 may solve this problem, see https://channel9.msdn.com/Events/Build/2015/3-635

XAML(通用应用)在windows 10和windows phone 10中的绑定(也称为“编译数据绑定”)可以解决这个问题,参见https://channel9.msdn.com/events/build5/3-635

I can't find the on line docs for it, but have not put much effort in, as it is something I will not be using for some time. However this answer should be a useful pointer to other people.

我找不到它的在线文档,但是没有花太多精力,因为它是我一段时间不会使用的东西。然而,这个答案应该是对其他人有用的指示。