Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
309 views
in Technique[技术] by (71.8m points)

c# - Extending a solution for simple binding to a 'Text property to multiple Controls to handle binding to any Type?

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";
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...