当我希望将控件绑定(bind)到对象的属性时,我必须以字符串形式提供属性名称。这不是很好,因为:
- 如果该属性被删除或重命名,则我不会收到编译器警告。
- 如果使用重构工具重命名属性,则数据绑定(bind)很可能不会更新。
- 如果属性类型错误,例如将整数绑定(bind)到日期选择器,然后在运行时之前我不会收到错误。
是否有一种设计模式可以解决这个问题,但仍然具有数据绑定(bind)的易用性?
(这是 WinForms、ASP.NET 和 WPF 以及可能的其他系统中的问题。)
我现在发现“workarounds for nameof() operator in C#: typesafe databinding”也有一个很好的解决方案起点。
如果您愿意在编译代码后使用后处理器,那么NotifyPropertyWeaver值得一看。
当绑定(bind)以 XML 而不是 C# 完成时,有人知道 WPF 的好的解决方案吗?
最佳答案
请注意,此答案使用 WinForm,并且是在 C# 具有“NameOf()”之前编写的
感谢 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(或 see this answer 实现 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;
}
WinForms 绑定(bind)助手类包含使它全部工作的内容:
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);
}
}
}
这利用了 C# 3.5 中的许多新内容,并展示了可能的内容。现在要是我们有hygienic macros就好了lisp 程序员可能会停止称我们为二等公民)
关于c# - 如何使数据绑定(bind)类型安全并支持重构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1329138/