如何使数据绑定类型安全并支持重构 [英] How to make Databinding type safe and support refactoring
问题描述
当我希望将控件绑定到对象的属性时,我必须以属性的形式提供字符串的名称。这不是很好,因为:
- 如果属性被删除或
重命名,我没有得到一个编译器
警告。 - 如果使用重构工具重命名财产
,则
可能会更新
。 - 如果财产
的类型错误,我直到
运行时才会收到错误,例如将一个整数绑定到
a日期选择器。
有没有一个设计模式,使用数据绑定?
(这是WinForm,Asp.net和WPF中的一个问题,很可能是很多其他系统)
我现在发现名称的解决方法()运算符在C#:typesafe数据绑定,这也是解决方案的一个很好的起点。
如果您愿意在编译后使用后处理器您的代码, notifypropertyweaver 非常值得一看。
< hr>
当绑定在XML而不是C#中完成时,任何人都知道一个很好的解决方案。
它的用法如下:
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:
- If the property is removed or renamed, I don’t get a compiler warning.
- If a rename the property with a refactoring tool, it is likely the data binding will not be updated.
- 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屋!