将简单绑定到“文本”属性的解决方案扩展到多个控件以处理与任何类型的绑定?

发布于 2024-08-24 06:14:01 字数 3364 浏览 8 评论 0原文

我的问题是:如何超越编写用于数据绑定多个控件(没有内置 DataSource 属性的控件)的技术的自定义实现,针对每种可能的数据类型,简单的属性...如下面的代码中所描述和演示...实现更强大的解决方案,该解决方案将独立于绑定是字符串、int还是其他类型。

我的猜测是:这将涉及反思;但是,我被困在这一点上。我正在寻找有关下一步移动“方向”的战略建议、提示、线索,而不是完整的代码答案,但当然我感谢所有回复,如果您在回复中发布代码,我一定会研究代码! Marc Clifton 2005 年关于 CodeProject 简单数据绑定 的文章:似乎演示了基于反射的方法:但是,老实说,我并没有真正理解他的代码,而且,就 .NET 而言,2005 年已经是很久以前的事了。

背景:部分是为了回应各种SO问题和答案,例如:以三种形式更新用户控件:我开发了一种成功的技术,可以将各种控件的文本属性同时绑定到公共类中定义的一个源;还能够使用定义一个扩展方法和两个公共方法的静态类来“抽象”绑定过程的一些细节。

我已经验证了“MainForm”中控件上的 TextBox、MainForm 上 UserControl 上的 TextBox 以及第二个窗体上的 TextBox“独立”打开(即,form2.Parent == null)都正确更新(即,两个-way 绑定有效)来自“DataSource 等效”公共类。改变一项:改变全部。

代码:此类的实例将为数据绑定提供目标属性 (theText):

    public class TextDataBinder
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _theText;

        public string theText
        {
            get { return _theText; }

            // note : if 'setter is declared 'internal : blocks 
            // auto-updating when run-time user modifies consumers
            // but will still allow update via code
            set
            {
                _theText = value;
                OnPropertyChanged(new PropertyChangedEventArgs("theText"));         
            }
        }

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }
    }

代码:此静态类可以隐藏一些绑定过程的复杂性,并允许轻松绑定到多个控件:

    public static class TextBindingExtender
    {
        public static TextDataBinder CurrentDataSource;

        public static void SetCurrentDataSource(TextDataBinder newCurrentDataSource)
        {
            CurrentDataSource = newCurrentDataSource;
        }

        // extension method for Control
        public static void AddTextBinding(this Control theControl, string controlPropertyName, string targetPropertyName)
        {
            theControl.DataBindings.Add(controlPropertyName, CurrentDataSource, targetPropertyName, false, DataSourceUpdateMode.OnPropertyChanged);
        }

        // bind to all Controls in a List<Control>
        public static void AddTextBindings(List<Control> theControls, string controlPropertyName, string targetPropertyName)
        {
            foreach (Control theControl in theControls)
            {
                theControl.AddTextBinding(controlPropertyName, targetPropertyName);
            }
        }
    }

如何使用上述类(在 Form 中)加载事件):

    // create a new TextDataBinder
    TextBindingExtender.CurrentDataSource = new TextDataBinder();

    // bind to multiple textboxes, label, on a UserControl, on another Form, etc.
    TextBindingExtender.AddTextBindings(new List<Control> { textBox1, textBox2, userControl11.tb, label1, instanceOfForm2.tb }, "Text", "theText");

    // test assigning some initial text to the bound property
    TextBindingExtender.CurrentDataSource.theText = "some initial text";

My question is : how to move beyond writing a custom implementation of a technique for databinding multiple controls (controls without built-in DataSource properties), for each possible type of data, to simple properties ... as described and demonstrated in code that follows ... to achieve a more poweful solution that will be independent of whether the binding is to a string, or an int, or other types.

My guess is: this will involve reflection; but, I'm stuck at that point. I'm looking for strategic advice on which "direction" to move next, hints, clues, not a complete code answer, but of course I appreciate all responses, and I'll sure study code if you post code in reply ! Marc Clifton's 2005 article on CodeProject Simple Databinding: appears to demonstrate a reflection based approach: but, honestly, I do not really grok his code, and, in terms of .NET, 2005 is a long time ago.

Background: Partly in response to various SO questions and answers, like: Update Usercontrol on Three Forms: I've evolved a successful technique for databinding text properties of various controls simultaneously to one source defined in a Public class; also been able to "abstract" some of the details of the binding process using a static class that defines one extension method, and two public methods.

I've verifed that TextBoxes on Controls in a "MainForm," TextBoxes on a UserControl on the MainForm, and a TextBox on a second Form opened "independently" (i.e., form2.Parent == null) all update properly (i.e., two-way binding is in effect) from the "DataSource equivalent" public class. Change one: change all.

Code: an instance of this class will supply the target property (theText) for databinding:

    public class TextDataBinder
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private string _theText;

        public string theText
        {
            get { return _theText; }

            // note : if 'setter is declared 'internal : blocks 
            // auto-updating when run-time user modifies consumers
            // but will still allow update via code
            set
            {
                _theText = value;
                OnPropertyChanged(new PropertyChangedEventArgs("theText"));         
            }
        }

        protected void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, e);
            }
        }
    }

Code: this static class enables hiding some of the binding process complexity, and allows easy binding to multiple controls:

    public static class TextBindingExtender
    {
        public static TextDataBinder CurrentDataSource;

        public static void SetCurrentDataSource(TextDataBinder newCurrentDataSource)
        {
            CurrentDataSource = newCurrentDataSource;
        }

        // extension method for Control
        public static void AddTextBinding(this Control theControl, string controlPropertyName, string targetPropertyName)
        {
            theControl.DataBindings.Add(controlPropertyName, CurrentDataSource, targetPropertyName, false, DataSourceUpdateMode.OnPropertyChanged);
        }

        // bind to all Controls in a List<Control>
        public static void AddTextBindings(List<Control> theControls, string controlPropertyName, string targetPropertyName)
        {
            foreach (Control theControl in theControls)
            {
                theControl.AddTextBinding(controlPropertyName, targetPropertyName);
            }
        }
    }

How the above classes are used (in a Form Load event) :

    // create a new TextDataBinder
    TextBindingExtender.CurrentDataSource = new TextDataBinder();

    // bind to multiple textboxes, label, on a UserControl, on another Form, etc.
    TextBindingExtender.AddTextBindings(new List<Control> { textBox1, textBox2, userControl11.tb, label1, instanceOfForm2.tb }, "Text", "theText");

    // test assigning some initial text to the bound property
    TextBindingExtender.CurrentDataSource.theText = "some initial text";

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

孤独陪着我 2024-08-31 06:14:01

这实际上取决于你想做什么;但最终常见的数据绑定(对于简单的属性,手动完成)包括:

  • 获取属性;最好通过 TypeDescriptor.GetProperties(obj)[propName],为您提供一个抽象 (PropertyDescriptor),
  • 询问属性是否为只读 (.IsReadOnly) code>)
  • 获取(或设置)值 (.GetValue().SetValue()),
  • 要求转换器格式化/解析该值 (.Converter.ConvertFromString().ConvertToString()) 这是一个关键位,意味着您不必担心什么数据类型要求
  • 其提供标题(.DisplayName,或 .Name,如果为空/null),
  • 询问其是否支持特定于属性的通知(.SupportsChangeEvents)
  • 要求它添加/删除更改处理程序 (.AddValueChanged().RemoveValueChanged()),
  • 您可能还想查看对象是否支持集中通知(查找INotifyPropertyChanged

如果您可能绑定到列表而不是单个对象:
- 列表可能被抽象在IListSource后面
- 该列表可能具有自定义属性,因此请检查 ITypedList
- 否则,识别项目的Type并使用TypeDescriptor.GetProperties(type)
- 您需要考虑一个“货币管理器”(即绑定到同一列表的所有内容都应该始终指向列表中的同一记录)

还有诸如 ICustomTypeDescriptor之类的东西>TypeDescriptionProvider 需要考虑,但大多数时候 TypeDescriptor 会自动为您处理此问题。

正如您所看到的 - 有很多事情需要考虑!很多工作......你不必做的一件事就是反思;这是在 PropertyDescriptor 后面抽象出来的。原因是并非所有数据都是静态类型的;考虑一下 DataTable - 列(映射到可绑定数据属性)在编译时并未固定,因此反射是不合适的。同样,其他一些类型也有自定义的“属性包”实现。 PropertyDescriptor 让您的代码能够以相同的方式处理动态(不是 4.0 意义上的)属性和反射属性。它也可以很好地与“HyperDescriptor”(另一种属性定制)等内容配合使用。

It really depends what you want to do; but ultimately common data-binding (for simple properties, done manually) consists of:

  • obtaining a property; preferably via TypeDescriptor.GetProperties(obj)[propName], giving you an abstraction (PropertyDescriptor)
  • asking the property if it is read-only (.IsReadOnly)
  • obtain (or set) the value (.GetValue(), .SetValue())
  • asking it for a converter to format / parse the value (.Converter, .ConvertFromString(), .ConvertToString()) THIS is a key bit that means you don't have to worry about what the data type is
  • asking it for the caption (.DisplayName, or .Name if that it empty/null)
  • asking it if it supports property-specific notification (.SupportsChangeEvents)
  • asking it to add/remove a change handler (.AddValueChanged(), .RemoveValueChanged())
  • you might also want to look at whether the object supports centralised notification (look for INotifyPropertyChanged)

If you might be binding to a list rather than a single object:
- the list might be abstracted behind IListSource
- the list might have custom properties, so check for ITypedList
- otherwise, identify the Type of the items and use TypeDescriptor.GetProperties(type)
- you need to consider a "currency manager" (i.e. should all the things bound to the same list be pointing to the same record in the list all the time)

There are also things like ICustomTypeDescriptor and TypeDescriptionProvider to consider, but most of the time TypeDescriptor handles this for you automatically.

As you can see - lots of things to think about! Lots of work... the one thing that you don't have to do is reflection; this is abstracted behind PropertyDescriptor. The reason for this is that not all data is static-typed; think about DataTable - the columns (which map to bindable data properties) are not fixed at compile-time, so reflection isn't appropriate. Likewise, some other types have custom "property bag" implementations. PropertyDescriptor lets your code handle either dynamic (not in the 4.0 sense) and reflective properties identically. It also works nicely with things like "HyperDescriptor", another property customisation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文