Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two Ways DataBinding in Winforms

Tags:

c#

I'm learning about databinding, I have a Class with one property, then I have another class with a combobox and 2 values "1 and 2", I've created an array object of my class with the property, so when combobox has 1 my textbox will give it a value to class[0].property, instead if I have 2 this happens class[1].property here is the code so you will understand better:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
    struct Class1
    {
        public string pollo { get; set; }
    }
}

My second class:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        Class1[] prova = new Class1[2];
        int a;

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            a = Convert.ToInt32(comboBox1.SelectedItem) - 1;
            prova[a].pollo = textBox1.Text;
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            a = Convert.ToInt32(comboBox1.SelectedItem) - 1;
            textBox1.DataBindings.Add("Text", prova[a], "pollo", false, DataSourceUpdateMode.OnPropertyChanged);
            textBox1.DataBindings.Clear();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            comboBox1.SelectedIndex = 0;
        }
    }
}

All works fine, but this is a one way databinding in fact I have to set the property with the click button and in this case there is no such difference between:

textBox1.DataBindings.Add("Text", prova[a], "pollo", false, DataSourceUpdateMode.OnPropertyChanged);

and

textBox1 = prova[a];

So why use a databinding? I mean how can I make it two ways so that automatically set my property?

Thanks

like image 620
Chrix1387 Avatar asked Feb 10 '23 04:02

Chrix1387


1 Answers

You have some problems in your code, preventing the binding from working correctly, and so obscuring the usefulness.

First, to be clear: the binding that is being set is between the currently selected Class1 object and the Text property of the TextBox. You are using the ComboBox to change the currently selected object to bind the TextBox to. I'm assuming you're aware of this, but I want to make sure.

Now, as far as the problems in the code go…

  1. The most serious problem is that your data type Class1 is being declared as a struct, rather than as a class. The struct type is a value type, meaning whenever code needs an object reference, a copy of the value is boxed (stored in an instance of object). It's very important to understand that this boxed value is a copy. It is completely disconnected from the value you've stored in your array, so even if the binding were successfully set, changes to the object would not be reflected elsewhere in the code where you retrieve the object value from the array.

  2. Almost as serious is that you clear the binding immediately after setting it. This completely negates the point of data binding, which is to allow the framework to automatically update property values based on changes in another object. So yes, in your code example there is literally no difference whatsoever between the set-binding-then-clear-binding operation and simply setting the property directly.

Either of these two problems is sufficient to prevent data binding from working in a useful way. But there is also a third problem…

  1. Your Class1 type does not implement a property changed event. In Winforms, you can implement either an event with the name polloChanged (i.e. the property name, followed by the word Changed, spelled and capitalized exactly like that), or by implementing the INotifyPropertyChanged interface. Without either of these mechanisms, two-way data binding cannot work, because the framework has no way to know when the value of the property has changed. (Ironically, what does work is the target-to-source binding…that is, because the TextBox class does implement the TextChanged event, the data binding is able to set the source property when the target property changes. But it doesn't go the other way).

Here is a version of your code that takes full advantage of data binding, implementing Class1 correctly (as an actual class, and with the necessary polloChanged event), configuring the binding correctly, and binding the object's pollo property to a Label so that it's clear the object's pollo property is being updated:

class Class1
{
    private string _pollo = "";
    public string pollo
    {
        get { return _pollo; }
        set
        {
            _pollo = value;
            Raise(polloChanged, this);
        }
    }

    private static void Raise(EventHandler handler, object sender)
    {
        if (handler != null)
        {
            handler(sender, EventArgs.Empty);
        }
    }

    public event EventHandler polloChanged;
}


public partial class Form1 : Form
{
    private Class1[] _prova =
    {
        new Class1 { pollo = "<not set 1>" },
        new Class1 { pollo = "<not set 2>" }
    };

    public Form1()
    {
        InitializeComponent();

        comboBox1.SelectedIndex = 0;
    }

    private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
    {
        // Obviously in a more complicated data binding scenario, you might
        // want to be more specific about which binding(s) is(are) being
        // removed, rather than just clearing everything.
        textBox1.DataBindings.Clear();
        label1.DataBindings.Clear();

        // If the user edits the text in the TextBox, the pollo property
        // of the currently-selected object will be immediately updated
        textBox1.DataBindings.Add("Text", _prova[comboBox1.SelectedIndex],
            "pollo", false, DataSourceUpdateMode.OnPropertyChanged);

        // We're never going to change the label1.Text property directly,
        // so the binding doesn't ever need to update the source property.
        label1.DataBindings.Add("Text", _prova[comboBox1.SelectedIndex],
            "pollo", false, DataSourceUpdateMode.Never);
    }
}

I assume you can infer the necessary textBox1, comboBox1, and label1 controls in the form, rather than having me post all the Designer code.


Finally, in case you prefer the INotifyPropertyChanged approach, here's what your Class1 would look like using that technique:

class Class1 : INotifyPropertyChanged
{
    private string _pollo = "";
    public string pollo
    {
        get { return _pollo; }
        set
        {
            _pollo = value;
            OnPropertyChanged();
        }
    }

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
like image 173
Peter Duniho Avatar answered Feb 13 '23 02:02

Peter Duniho