如何使数据绑定类型安全并支持重构 [英] How to make Databinding type safe and support refactoring

查看:169
本文介绍了如何使数据绑定类型安全并支持重构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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


  1. 如果属性被删除或
    重命名,我没有得到一个编译器
    警告。

  2. 如果使用重构工具重命名财产
    ,则
    可能会更新

  3. 如果财产
    的类型错误,我直到
    运行时才会收到错误,例如将一个整数绑定到
    a日期选择器。

有没有一个设计模式,使用数据绑定?



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



我现在发现名称的解决方法()运算符在C#:typesafe数据绑定,这也是解决方案的一个很好的起点。



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



< hr>

当绑定在XML而不是C#中完成时,任何人都知道一个很好的解决方案。

解决方案感谢Oliver让我开始,我现在有了一个解决方案支持重构,类型安全。它也让我实现INotifyPropertyChanged,以便处理重命名的属性。



它的用法如下:

  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);

该人员课程显示了如何以类型安全的方式实现INotifyPropertyChanged(或看到这个答案,用于实现INotifyPropertyChanged, ActiveSharp - 自动INotifyPropertyChanged 也看起来不错):

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

// etc

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

public event PropertyChangedEventHandler PropertyChanged;
}

WinForms绑定帮助器类中的内容使其全部工作:

 命名空间TypeSafeBinding 
{
public static class BindingHelper
{
private static字符串GetMemberName(表达式表达式)
{
//名称操作符是在2015年7月的.NET 4.6
//和VS2015的C#6.0中实现的。
//以下是仍然适用于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://更改
return String.Empty;
default:
throw new ArgumentException(表达式不是成员访问或方法调用表达式);
}
}

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

// NEW
public static string Name< T>(Expression< Func< T>>表达式)
{
return GetMemberName表达。
}

public static void Bind {
control.DataBindings.Add(Name(controlProperty),dataSource,Name(dataMember));
}

public static void BindLabelText< T>(此Label控件,T dataObject,Expression&FunC< T,object>> dataMember)
{
/ /因为这是一种任何类型的属性是ok
control.DataBindings.Add(Text,dataObject,Name(dataMember));
}

public static void BindEnabled< T>(此控件控件,T dataObject,Expression< Func< T,bool>> dataMember)
{
控件.Bind(c => c.Enabled,dataObject,dataMember);
}
}
}

这使得很多的C#3.5中的新东西,并显示出什么是可能的。现在如果只有我们有卫生宏,lisp程序员可能会停止给我们打电话给二等公民)


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. If a rename the property with a refactoring tool, it is likely the data binding will not be updated.
  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.

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)

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

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


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

解决方案

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.

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 ):

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:

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)

这篇关于如何使数据绑定类型安全并支持重构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆