强类型的 Windows Forms 数据绑定

8

我正在研究使用扩展方法进行强类型的Windows Forms数据绑定。在得到Xavier的帮助后,我已经完成了以下步骤:

using System;
using System.Linq.Expressions;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public static Binding Add<T>
        (this ControlBindingsCollection dataBindings,
            object dataSource,
            Expression<Func<Control, object>> controlExpression,
            Expression<Func<T, object>> objectExpression)
    {
        return Add(dataBindings, dataSource, controlExpression, objectExpression, false);
    }

    public static Binding Add<T>
        (this ControlBindingsCollection dataBindings,
            object dataSource,
            Expression<Func<Control, object>> controlExpression,
            Expression<Func<T, object>> objectExpression,
            bool formattingEnabled)
    {
        string controlPropertyName = ProcessExpression(controlExpression.Body);
        string bindingTargetName = ProcessExpression(objectExpression.Body);

        return dataBindings
            .Add(controlPropertyName, dataSource, bindingTargetName, formattingEnabled);
    }

    public static Binding Add<T, K>
        (this ControlBindingsCollection dataBindings,
            object dataSource,
            Expression<Func<K, object>> controlExpression,
            Expression<Func<T, object>> objectExpression)
    {
        return Add(dataBindings, dataSource, controlExpression, objectExpression, false);
    }

    public static Binding Add<T, K>
        (this ControlBindingsCollection dataBindings,
            object dataSource,
            Expression<Func<K, object>> controlExpression,
            Expression<Func<T, object>> objectExpression,
            bool formattingEnabled
        )
    {
        string controlPropertyName = ProcessExpression(controlExpression.Body);
        string bindingTargetName = ProcessExpression(objectExpression.Body);

        return dataBindings.Add(controlPropertyName, dataSource, bindingTargetName, formattingEnabled);
    }

    private static string ProcessExpression(Expression expression)
    {
        string propertyName;
        if (expression is MemberExpression)
        {
            propertyName = ((MemberExpression) (expression)).Member.Name;
        }
        else if (expression is UnaryExpression)
        {
            propertyName = ((MemberExpression) ((UnaryExpression) (expression)).Operand).Member.Name;
        }
        else
        {
            throw new InvalidOperationException(
                "Unknown expression type error in DataBindingsExtensionMethods.Add<T, K>");
        }
        return propertyName;
    }
}

现在我可以像这样设置数据绑定:
txtBoundInt.DataBindings.Add<Contact>
    (bindingSource, tb => tb.Text, contact => contact.Id);

或者这个:
cboBoundSelectedItem.DataBindings.Add
            <Contact, ComboBox>
            (bindingSource, cbo => cbo.SelectedItem, con => con.ContactType)

看起来有很多表达式的转换正在进行中。是否有更好的方法?


编辑:我找到了一个更好的方法,但因为将这个问题改成了那个答案而陷入麻烦 - 它由@Carl_G 在下面复制


请不要将您的问题修改为答案。如果您已经找到了解决方案,它需要放在答案部分。对于试图快速浏览谷歌链接以寻找解决方案的人来说,阅读“好的,我找到了一个解决方案”而不知道您的问题是什么,或者无法评估它是否适用于访问者的问题,这是非常令人迷惑的。 - Asad Saeeduddin
哦,好吧,得按照规则来玩。 - stuartd
1
需要注意的是,C# 6中的新nameof()函数也可以用于避免使用字符串。https://msdn.microsoft.com/en-us/library/dn986596.aspx - Tim Friesen
3个回答

6
设置返回类型为对象怎么样?
public static Binding Add<T>
    (this ControlBindingsCollection dataBindings, object dataSource,
    Expression<Func<Control, object>> controlLambda,
    Expression<Func<T, object>> objectLambda) {
    string controlPropertyName =
          ((MemberExpression)(controlLambda.Body)).Member.Name;
    string bindingTargetName =
          ((MemberExpression)(objectLambda.Body)).Member.Name;

    return dataBindings.Add
         (controlPropertyName, dataSource, bindingTargetName);
}

谢谢:它可以编译,但产生了这个运行时错误:无法将类型为'System.Linq.Expressions.UnaryExpression'的对象强制转换为类型'System.Linq.Expressions.MemberExpression'。 - stuartd
嗯。我可以将objectLambda转换为UnaryExpression,但我不知道如何从UnaryExpression中获取属性名称..(controlLambda仍然是MemberExpression) - stuartd
异常 无法将类型为 'System.Linq.Expressions.UnaryExpression' 的对象强制转换为类型 'System.Linq.Expressions.MemberExpression'。 通常发生在函数中的类型不再匹配时,例如当您有 Expression<Func<T, int>> ... x -> x.ID 并且 x.ID 实际上是 long 类型时。 - Seph

6

由于问题被编辑为仅包含答案,因此我在此处包含该答案。作者可能应该保持原始问题不变,并对自己的问题进行回答。但这似乎是一个非常好的解决方案。


编辑:我更喜欢最终找到的这个解决方案(在Google的缓存中)(已从作者的网站中删除),因为它只需要一个类型规范。我不知道原作者为什么删除了它。

// Desired call syntax:
nameTextBox.Bind(t => t.Text, aBindingSource, (Customer c) => c.FirstName);

// Binds the Text property on nameTextBox to the FirstName property
// of the current Customer in aBindingSource, no string literals required.

// Implementation.

public static class ControlExtensions
{
    public static Binding Bind<TControl, TDataSourceItem>
        (this TControl control, 
         Expression<Func<TControl, object>> controlProperty, 
         object dataSource, 
         Expression<Func<TDataSourceItem, object>> dataSourceProperty)
         where TControl: Control
    {
        return control.DataBindings.Add
             (PropertyName.For(controlProperty), 
              dataSource, 
              PropertyName.For(dataSourceProperty));
    }
}

public static class PropertyName
{
    public static string For<T>(Expression<Func<T, object>> property)
    {
        var member = property.Body as MemberExpression;
        if (null == member)
        {
            var unary = property.Body as UnaryExpression;
            if (null != unary) member = unary.Operand as MemberExpression;
        }
        return null != member ? member.Member.Name : string.Empty;
    }
}

3
SO采用问答格式,这样有相同问题/疑问的人可以搜索他们的问题/疑问,并从中受益或提供解决方案。我尊重不同用户的风格喜好,但我强烈反对您完全删除原始问题。您现在替换的答案缺乏问题所提供的所有上下文信息。这不仅违反了SO的设计,而且使其他人难以从中受益。 - Carl G
1
不,它只是将答案放在正确的位置。因此,如果有人决定“好的,我认为我明白了这个问题是什么,现在我要浏览答案,看看是否有任何好的答案”,他们可以看到你包含的好答案。 - Carl G
1
你看到我把你最初的问题放在前面,然后是你的回答吗?(现在只剩下回答了,就像你以前放置的那样。)我想也许你已经还原了它,但也许是管理员还原了它。我不知道,我只是想帮忙。 - Carl G
1
我说的第一件事是,我已经从不再是问题的问题中复制了我的答案内容。我真的看不出你的观点。 - Carl G
1
不好意思,在急于阅读您的回复时,我错过了问题已经被回退的事实(您实际上已经说过,但是我误解了)。我认为您提供的解决方案必须以某种形式保留,因为它很好。如果您想在自己的帐户下发布它,请这样做,然后我也可以删除我的答案。我只是希望人们从 SO 中受益。还记得谷歌搜索编程问题只返回 Expert Exchange 付费墙后面的可怕日子吗? - Carl G
1
这对我很有效,我喜欢这个解决方案。我更新了TControl类型约束以使用IBindableComponent。并将“object dataSource”参数更改为使用“TDataSourceItem dataSource”,这意味着您不必转换搜索所需属性的lambda函数。 - ShaunO

2
我已经使用Stuart发布的代码几个月了。为了匹配您可能想要使用的其它数据绑定方案,我添加了一些重载。我在这里发布它,让其他人更轻松地使用这个非常有用的东西。
    public static class ControlExtensions {

    /// <summary>Databinding with strongly typed object names</summary>
    /// <param name="control">The Control you are binding to</param>
    /// <param name="controlProperty">The property on the control you are binding to</param>
    /// <param name="dataSource">The object you are binding to</param>
    /// <param name="dataSourceProperty">The property on the object you are binding to</param>
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty));
    }
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty, bool formattingEnabled = false)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty), formattingEnabled);
    }
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty, bool formattingEnabled, DataSourceUpdateMode updateMode)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty), formattingEnabled, updateMode);
    }
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty), formattingEnabled, updateMode, nullValue);
    }
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue, string formatString)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty), formattingEnabled, updateMode, nullValue, formatString);
    }
    public static Binding Bind<TControl, TDataSourceItem>(this TControl control, Expression<Func<TControl, object>> controlProperty, object dataSource, Expression<Func<TDataSourceItem, object>> dataSourceProperty, bool formattingEnabled, DataSourceUpdateMode updateMode, object nullValue, string formatString, IFormatProvider formatInfo)
    where TControl :Control {
        return control.DataBindings.Add(PropertyName.For(controlProperty), dataSource, PropertyName.For(dataSourceProperty), formattingEnabled, updateMode, nullValue, formatString, formatInfo);
    }

    public static class PropertyName {
        public static string For<T>(Expression<Func<T, object>> property) {
            var member = property.Body as MemberExpression;
            if(null == member) {
                var unary = property.Body as UnaryExpression;
                if(null != unary) member = unary.Operand as MemberExpression;
            }
            return null != member ? member.Member.Name : string.Empty;
        }
    }

}

2
这段代码运行良好,但需要一个小的修复。它应该限制在“IBindableComponent”而不是“Control”上。这是具有“DataBindings”属性的正确接口。 - craig.tadlock

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接