如何使数据绑定类型安全并支持重构

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


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

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

如果属性的类型错误,直到运行时我都不会出错,例如,将整数绑定到日期选择器。

是否有一种设计模式可以解决此问题,但仍易于使用数据绑定?


(这是WinForm,Asp.net和WPF以及大多数其他系统中的问题)


现在,我发现“ C#中的nameof()运算符的变通办法:typesafe数据绑定 ”也为解决方案提供了良好的起点。


如果您愿意在编译代码后使用后处理器,那么notifypropertyweaver非常值得研究。


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


RISEBY
浏览 381回答 3
3回答

largeQ

感谢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);person类显示了如何以一种类型安全的方式实现INotifyPropertyChanged(或参见以下答案以实现实现INotifyPropertyChanged的另一种相当不错的方法,ActiveSharp-自动INotifyPropertyChanged也看起来不错):public class Person : INotifyPropertyChanged{&nbsp; &nbsp;private bool _employed;&nbsp; &nbsp;public bool Employed&nbsp; &nbsp;{&nbsp; &nbsp; &nbsp; get { return _employed; }&nbsp; &nbsp; &nbsp; set&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;_employed = value;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;OnPropertyChanged(() => c.Employed);&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp;}&nbsp; &nbsp;// etc&nbsp; &nbsp;private void OnPropertyChanged(Expression<Func<object>> property)&nbsp; &nbsp;{&nbsp; &nbsp; &nbsp; if (PropertyChanged != null)&nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PropertyChanged(this,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;new PropertyChangedEventArgs(BindingHelper.Name(property)));&nbsp; &nbsp; &nbsp; }&nbsp; &nbsp;}&nbsp; &nbsp;public event PropertyChangedEventHandler PropertyChanged;}WinForms绑定帮助程序类包含了使它们全部工作的基础:namespace TypeSafeBinding{&nbsp; &nbsp; public static class BindingHelper&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; private static string GetMemberName(Expression expression)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // The nameof operator was implemented in C# 6.0 with .NET 4.6&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // and VS2015 in July 2015.&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // The following is still valid for C# < 6.0&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; switch (expression.NodeType)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ExpressionType.MemberAccess:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var memberExpression = (MemberExpression) expression;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var supername = GetMemberName(memberExpression.Expression);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (String.IsNullOrEmpty(supername)) return memberExpression.Member.Name;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return String.Concat(supername, '.', memberExpression.Member.Name);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ExpressionType.Call:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var callExpression = (MethodCallExpression) expression;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return callExpression.Method.Name;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ExpressionType.Convert:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var unaryExpression = (UnaryExpression) expression;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return GetMemberName(unaryExpression.Operand);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ExpressionType.Parameter:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; case ExpressionType.Constant: //Change&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return String.Empty;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; default:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new ArgumentException("The expression is not a member access or method call expression");&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; public static string Name<T, T2>(Expression<Func<T, T2>> expression)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return GetMemberName(expression.Body);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; //NEW&nbsp; &nbsp; &nbsp; &nbsp; public static string Name<T>(Expression<Func<T>> expression)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return GetMemberName(expression.Body);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; 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&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; control.DataBindings.Add(Name(controlProperty), dataSource, Name(dataMember));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; public static void BindLabelText<T>(this Label control, T dataObject, Expression<Func<T, object>> dataMember)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // as this is way one any type of property is ok&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; control.DataBindings.Add("Text", dataObject, Name(dataMember));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; public static void BindEnabled<T>(this Control control, T dataObject, Expression<Func<T, bool>> dataMember)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;control.Bind(c => c.Enabled, dataObject, dataMember);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}这利用了C#3.5中的许多新内容,并显示了可能的结果。现在,如果只有卫生宏,那么 Lisp程序员可能会停止称我们为二等公民)

天涯尽头无女友

Framework 4.5为我们提供了CallerMemberNameAttribute,从而无需将属性名称作为字符串传递:private string m_myProperty;public string MyProperty{&nbsp; &nbsp; get { return m_myProperty; }&nbsp; &nbsp; set&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; m_myProperty = value;&nbsp; &nbsp; &nbsp; &nbsp; OnPropertyChanged();&nbsp; &nbsp; }}private void OnPropertyChanged([CallerMemberName] string propertyName = "none passed"){&nbsp; &nbsp; // ... do stuff here ...}如果您正在使用安装了KB2468871的 Framework 4.0进行操作,则可以通过nuget来安装Microsoft BCL兼容性包,该包也提供了此属性。
打开App,查看更多内容
随时随地看视频慕课网APP