TextBox podpięty do BindingSource, TextBox.Text nie da się zmienić

0

Mam problem z edycją tekstu w TextBox'ach na formularzu (WindowsForm). Co robię źle i jak to poprawić?

Gdy próbuje dodać tekst, skasować tekst w kontrolce, kontrolka na chwilę przyjmuję zmianę, potem szybko wraca stara wartość (text) przed moją ingerencją i kursor ustawia się na początku tekstu w kontrolce. Najprawdopodobniej miesza tu obsługa zdarzenia TextChanged ale nie mogę dojść w którym momencie. TextChanged ponadto odpala się zaraz gdy faktycznie zmieniam tresc w któryms TextBoxie, przechodzi cała metoda GenerujLogin() i potem znów odpala sie to zdarzenie, nie wiem dlaczego.
Nie do końca rozumiem bindowanie, jak, kiedy i z wywoływaniem jakich zdarzeń dane w BindingSource są aktualizowane i jak, kiedy z BindingSource sa aktualizowane TextBoxy podpiete do BindingSource. Jakby ktoś mógł dopoiwiedzieć.

Po otworzeniu formularza, do TextBox'ów przypisywane są wartości tak:

 
txtBox_Imie1.DataBindings.Add("Text", WybranyPracownik, "Imie1", false, DataSourceUpdateMode.OnPropertyChanged);
txtBox_Imie2.DataBindings.Add("Text", WybranyPracownik, "Imie2", false, DataSourceUpdateMode.OnPropertyChanged);
txtBox_Nazwisko.DataBindings.Add("Text", WybranyPracownik, "Nazwisko", false, DataSourceUpdateMode.OnPropertyChanged);
txtBox_Login.DataBindings.Add("Text", WybranyPracownik, "Login", false, DataSourceUpdateMode.OnPropertyChanged);

WybranyPracownik jest ustawiany w konstruktorze Okna, Pracownik jest przekazywamy z okna głownego w parametrze konstruktora podokna, a sama właściwość wygląda tak:

 
public Pracownik WybranyPracownik
        {
            get
            {
                return this.pracownikBindingSource.Current as Pracownik;
            }
            set
            {
                this.pracownikBindingSource.DataSource = value;
            }
        }

a samo zdarzenie TextChanged wyglada tak:

 
        private void txtBoxy_ImieniaINazwiska_TextChanged(object sender, EventArgs e)
        {
            txtBox_Login.Text = GenerujLogin(txtBox_Imie1.Text, txtBox_Imie2.Text, txtBox_Nazwisko.Text);
            if (CzyLoginuNieMaWBazie(txtBox_Login.Text))
            {                
                 errorProvider.Clear();
            }
            else
            {
                 errorProvider.SetError(txtBox_Login, "Taki login już występuje w bazie");
            }
        }

a samo generowanie loginu wygląda tak:

 
public string GenerujLogin(string imie1, string imie2, string nazwisko)
        {
            System.Text.StringBuilder sb = new System.Text.StringBuilder();

            if (!string.IsNullOrEmpty(imie1) && !string.IsNullOrEmpty(nazwisko))
            {
                imie1 = imie1.Trim();
                imie1 = imie1.ToLower();
                nazwisko = nazwisko.Trim();
                nazwisko = nazwisko.ToLower();
                imie1 = imie1.First().ToString().ToUpper() + String.Join("", imie1.Skip(1));
                nazwisko = nazwisko.First().ToString().ToUpper() + String.Join("", nazwisko.Skip(1));
            }

            sb.Append(imie1);
            sb.Append(" ");

            if (!string.IsNullOrEmpty(imie2))
            {
                imie2 = imie2.Trim();
                imie2 = imie2.ToLower();
                imie2 = imie2.First().ToString().ToUpper() + String.Join("", imie2.Skip(1));

                sb.Append(imie2);
                sb.Append(" ");
            }            
            
            sb.Append(nazwisko);

            return sb.ToString();
        }

Na tych TextBoxach robie jeszcze walidację od razu podczas wpisywania:

 
private void txtBox_Imie1_Leave(object sender, EventArgs e)
        {
            TextBox box = (TextBox)sender;
            if (this.CzyPoprawneImie(box.Text))
            {
                errorProvider.SetError(box, "Pole nie może być puste i dłuższe niż 20 znaków");
            }
            else
            {
                errorProvider.Clear();
            }
        }
0

Do których textboxów masz podpięte txtBoxy_ImieniaINazwiska_TextChanged ?? Czy nie jest on przypadkiem podpięty do Loginu?

1

po pierwsze - liczba mnoga od "Imię" to "Imiona", nie "Imienia"

po drugie - w ogóle nie korzystasz z this.pracownikBindingSource - pomijasz go, korzystając tylko z jego własności DataSource
równie dobrze mógłbyś zastąpić swój propert w ten sposób:

public Pracownik WybranyPracownik { get; set; }

i byłoby to samo
w ogóle czemu przypinasz bindowanie w kodzie a nie w designerze?

ale to wszystko nieistotne - wracając do problemu:

dzieje się tak ponieważ zmieniasz w OnChanged wartość innego textboxa który jest zbindowany

gdy wprowadzasz dane do kontrolki zostają one tylko w kontrolce, dopiero gdy kontrolka straci Focusa, dane są walidowane (leci event OnValidation) i przy poprawnej walidacji dane są wpisywane do obiektu

teraz w drugą stronę - jeśli zmienisz jakąś własność obiektu, wywołujesz zapewne zdarzenie PropertyChanged dając znać że propert się zmienił
gdy poleci zdarzenie PropertyChanged, bindingSource aktualizuje WSZYSTKIE swoje wartości wywołując get na każdym jednym propercie i wpisując dane do kontrolki

teraz gdy w obiekcie masz dane:

Imie: abc
Nazwisko: def
Login: ghi

i zmienisz w kontrolce imię na "ghi", poleci event TextChanged
wewnątrz niego zmieniasz login na "ghi def", w tym momencie jako że kontrolka nie ma focusa, następuje natychmiastowa walidacja nowej wartości "ghi def", po której następuje wpisanie jej do properta "Login"
w tym miejscu leci event PropertyChanged, bindingSource pobiera dane ze wszystkich propertów i ustawia "świeże" dane w kontrolkach

zauważ że w tym miejscu wartość w kontrolce "imię" nie jest jeszcze zaktualizowana na obiekcie i zostaje nadpisana starą wartością

rozwiązań masz parę - możesz przerzucić akcję z TextChanged do Validated a walidację zrobić w Validating, albo wpisać do obiektu nową wartość ręcznie poprzez:

WybranyPracownik.Imie = txtBox_imie.Text;

możesz też w setterze Imie i Nazwisko zmieniać Login

0

Właśnie w ten sposób sobie poradziłem że rozbiłem obsługe zdarzenia TextChanged na 3 do każdego TextBox'a oddzielnie i przed wywołaniem GenerujLogin() przypisuję ręcznie

 WybranyPracownik.Imie = txtBox_imie.Text; 

Ale dzieki za podpowiedz pokombinuje to wszystko jakoś pozwijać prościej i wg tego co napisałeś.

1 użytkowników online, w tym zalogowanych: 0, gości: 1