niedziela, 4 lipca 2010

Bindowanie danych w WindowsForms

Bindowanie danych, czyli na polski wiązanie danych z kontrolką to proces w którym pobieramy dane i łączymy je z kontrolką na formie.

Wiązanie takie może być proste, złożone oraz w jedną stronę jak i w dwie strony.

Przyjrzyjmy się najpierw prostemu wiązaniu z kontrolką typu TextBox. Proste wiązanie polega na zbindowaniu źródła danych z jedną lub wieloma właściwościami kontrolki. Napiszemy klasę z którą następnie zbindujemy TextBox:

    public class TextBoxBinding
    {
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        private Color _textColor;

        public Color TextColor
        {
            get { return _textColor; }
            set { _textColor = value; }
        }

        private Color _backColor;

        public Color BackColor
        {
            get { return _backColor; }
            set { _backColor = value; }
        }
    }

Sam kod bindowania wygląda natomiast tak:

            TextBoxBinding binding = new TextBoxBinding()
            {
                BackColor = Color.Blue,
                TextColor = Color.Red,
                Text = "Simple Binding"
            };

            textBox1.DataBindings.Add("Text", binding, "Text");
            textBox1.DataBindings.Add("ForeColor", binding, "TextColor");
            textBox1.DataBindings.Add("BackColor", binding, "BackColor");

DataBindings jest typu kolekcja. Co za tym idzie dodajemy kolejne elementy typu Binding lub tak jak pokazano wprowadzamy od razu wartości w kolejności:
textBox1.DataBindings.Add("Właściwość kontrolki, którą bindujemy ze źródłem", obiekt zawierający dane bindowania, "właściwość obiektu z którym bindujemy właściwość kontrolki");
Ostatni parametr możemy pominąć (null), jeżeli klasa z którą bindujemy kontrolkę zawiera przeładowaną metodę ToString().

Daną właściwość kontrolki można bindować tylko raz. W przypadku wywołania poniższego kodu otrzymamy error:

            textBox1.DataBindings.Add("Text", binding, "Text");
            binding.Text = "Ala ma kota";
            textBox1.DataBindings.Add("Text", binding, "Text");

Aby zbindować ponownie właściwość Text kontrolki TextBox, należy usunąć wpierw stare bindowanie a następnie przypisać nowe:

            textBox1.DataBindings.Add("Text", binding, "Text");
            binding.Text = "Ala ma kota";
            if (textBox1.DataBindings["Text"] != null)
            {
                textBox1.DataBindings.Remove(textBox1.DataBindings["Text"]);
            }
            textBox1.DataBindings.Add("Text", binding, "Text");

W przypadku bindowania do kolekcji obiektów należy wskazać do którego z obiektów ma zostać zbindowana dana właściwość:

            TextBoxBinding binding = new TextBoxBinding()
            {
                BackColor = Color.Blue,
                TextColor = Color.Red,
                Text = "Simple Binding"
            };
            List<TextBoxBinding> arrBinding = new List<TextBoxBinding>
            {
                binding,
                new TextBoxBinding { BackColor = Color.PowderBlue, Text = "Ala ma kota", TextColor = Color.Black},
                new TextBoxBinding { BackColor = Color.Brown, TextColor = Color.White, Text = "A kot ma Ale"}
            };

            textBox1.DataBindings.Add("Text", arrBinding[1], "Text");

Jeżeli tego nie zrobimy, zostanie wybrany domyślnie element o indeksie 0 z kolekcji.

Przejdźmy teraz do złożonego bindowania. Stworzymy na potrzeby tego przykładu nową klasę:

    class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int YearOfBirth { get; set; }
        public Person(string firstName, string lastName, int yearOfBirth)
        {
            FirstName = firstName;
            LastName = lastName;
            YearOfBirth = yearOfBirth;
        }
    }

Na formę kładziemy dwa TextBoxy oraz ListBox oraz przycisk. W kodzie dla eventu Click w przycisku wprowadzamy kod:

            List<Person> people = new List<Person>
            {
                new Person("Jan", "Kowlaski", 1998),
                new Person("Sebastian", "Potaciński", 2001),
                new Person("Mariusz", "Rozberg", 2001),
                new Person("Piotrek", "Simple", 1998),
                new Person("Łukasz", "Niewadzki", 1999),
                new Person("Maria", "Prowadzki", 1965),
            };

            listBox1.DataSource = people;
            listBox1.DisplayMember = "FirstName";
            textBox1.DataBindings.Add("Text", people, "LastName");
            textBox2.DataBindings.Add("Text", people, "YearOfBirth");

Uruchamiamy aplikację. Dzięki takiemu bindowaniu, w ListBoxie widzimy imiona osób a szczegóły o nich podawane są w TextBoxach. Taki widok osiągamy dzięki Binding Manager, który zarządza bindowanymi elementami. Należy pamiętać, że zaawansowane wiązanie działa tylko z kontrolkami które mogą wyświtelać na raz wiele elementów (ListBox, ListView, ComboBox itd.).

Teraz czas na bindowanie w jedną i wiele stron. Jak można się domyślić bindowanie w jedną stronę to po prostu umożliwienie przeglądania danych z jakiegoś źródła. W przypadku bindowania w obie strony, uaktualniając wyświetlaną wartość w kontrolce uaktualniamy także dane w źródle danych:


W przypadku zwykłych obiektów sprawa jest bardzo prosta. Aby umożliwić dwustronną wymianę danych, należy umożliwić zapis danych do danego pola, czyli w naszym przypadku udostępnić metody set i get:

        public int YearOfBirth { get; set; }

Jeżeli teraz spróbujemy zmienić imię którejś z osób, zmiana zostanie od razu zapisana. Jeżeli daną właściwość oznaczymy jako prywatną:

        public string LastName { get; private set; }

po wprowadzeniu nowej wartości do kontrolki nie zostanie ona zapisana do źródła. Ważne jest uświadomienie sobie, że zapis następuje dopiero w momencie kiedy opuszczamy kontrolkę (przechodzimy do kolejnej).

Kiedy bindujemy kontrolkę do danych z bazy danych, należy samemu zatroszczyć się o uaktualnianie danych w tym źródle. Można to osiągnąć np. poprzez użycie DataAdaptera i wywołanie metody Update dla danego DataSetu i tabeli.

Kontrolką która nadaje się świetnie do bindowania danych tabelarycznych jest DataGridView. Pozwala na szeroki wachlarz zastosowań wyświetlania jak i manipulowania danymi.
Oprócz wymienionych cech DataGridView może bindować bardzo duże ilości danych w krótkim czasie. Dodanie nowego wiersza do źródła danych uaktualnia samoczynnie kontrolkę. Dzięki trybowi VirtualMode pozwala na łatwe zarządzanie bardzo dużymi ilościami wierszy.

3 komentarze:

  1. Hej, świetny wpis, bardzo dobrze wyjaśnione wszystko krok po kroku :) dzięki!

    OdpowiedzUsuń
  2. opisane bindowanie do enuma z opisami jako atrybuty:
    https://blog.robertolechowski.com/bindowanie-enum/

    OdpowiedzUsuń
  3. Przydatny artykuł, dzięki :)

    --
    Pozdrawiam,
    Cyberiusz

    OdpowiedzUsuń