Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc

Przemyślenia dotyczące kontrolek Windows Forms

Chciałem się z Wami podzielić moimi przemyśleniami, które są wynikiem mojej dotychczasowej pracy z aplikacjami Windows Forms. Na początku krótkie streszczenie:

standardowe kontrolki Windows Forms dostarczane z .Net Framework są do bani!
 
Rzecz będzie o dwóch kontrolkach, z którymi miałem najwięcej do czynienia, a mianowicie DateTimePicker oraz NumericUpDown. Kiedyś rozmawiając z programistą jednej z firm we Wrocławiu, tworzących oprogramowanie usłyszałem, że owszem piszą w .Net i wykorzystują standardowe kontrolki, ale opakowują je własnymi klasami. Wtedy jeszcze nie widziałem konkretnego zastosowania takiej taktyki. Teraz po zmaganiach z ww. kontrolkami zauważam potencjalne korzyści. W dużych projektach mamy mnóstwo wymagań biznesowych, które dokładnie mówią jak ma przebiegać proces interakcji z kontrolką. Jak się okazało nie zawsze zachowanie danej kontrolki jest zgodne z naszymi oczekiwaniami. Wtedy zaczynają się zmagania…
 
Bitwa no.1. Walczyłem ostatnio z kontrolką NumericUpDown. Okazało się, że ustawienie jej własności określającej ilość miejsc po przecinku oraz maksymalnej wartości nie do końca się sprawdza. Wartości te są aplikowane dopiero w czasie walidacji zawartości kontrolki, natomiast nie blokują wpisywania. Dlatego, mimo że ustawiłem 4 miejsca po przecinku, użytkownik może z powodzeniem wpisać 10 cyfr i zapisać edytowany dokument będąc przekonanym, że wprowadzona wartość jest poprawna (numeric oczywiście obetnie te nadmiarowe miejsca podczas walidacji przy zapisywaniu). Jak się okazało takie zachowanie nie jest akceptowalne i musiałem sobie z tym sam poradzić. Ręczne wywołanie parsowania wprowadzonego tekstu okazało się w miarę proste. Problem pojawił się, gdy trzeba było blokować wpisywanie więcej niż ustalona liczba miejsc po przecinku zachowując pozycję kursora w odpowiednim miejscu. Uważam, że nie ma rzeczy niemożliwych dlatego i z tym sobie poradziłem. Wykorzystując Reflectora obejrzałem sobie jak wygląda numeric od wewnątrz i dowiedziałem się, że składa się między innymi z TextBoxa, który jest prywatny. Mechanizm refleksji pozwolił mi dobrać się do tego textboxa i ustawiać na nim kursor jak mi się podoba. W wyniku moich zmagań powstała kontrolka, która dziedziczy po standardowym numericu i zmienia jego zachowanie.
 
Bitwa no.2. Dostosowałem też DateTimePickera. Podczas pracy ze standardowym DTP wynikło kilka problemów. Pierwszym i najbardziej przeszkadzającym jest fakt, że kontrolka ta zmienia aktualnie wybraną datę i wbija ją do zbindownej pod spodem encji za każdym razem gdy:
  • Zmieni się rok, miesiąc lub dzień wpisując je ręcznie (przy czym wystarczy zmienić tylko jedną z tych części daty).
  • Gdy otworzy się kalendarz i zmieni się miesiąc jedną ze strzałek w prawym i lewym górnym rogu.
Takie zachowanie nie tylko mi przeszkadzało, ale także denerwowało. Przede wszystkim chodziło o walidację wprowadzonej daty. W moim przypadku każda zmiana wiązała się z pytaniem użytkownika o to czy zmienić również tą datę na innym elemencie. Oczywistym jest fakt, że nie mogłem wykorzystać zdarzenia ValueChanged. Więc dobrze, myślę sobie, zdarzenie Validated powinno pomóc. Pomogło, ale na krótko. Okazało się, że w jednym przypadku się nie sprawdza. Gdy nie zmienimy daty i opuścimy kontrolkę będziemy mieli walidację i ni jak nie będziemy w stanie sprawdzić czy data się zmieniła czy nie (chyba że o czymś nie wiem). Rozwiązanie ze zmienną walającą się gdzieś w kodzie i pamiętającą ostatnio wybraną datę w ogóle mnie nie zadowalało.
Kolejnym problemem było sprawdzenie czy podana przez użytkownika data mieści się w zadanym przedziale. Wykorzystywałem do tego właściwości MinDate i MaxDate. Wszystko byłoby dobrze, gdyby nie pewien przypadek. Mając przedział czasu, który rozpina się na kilka lat występuje problem z wprowadzaniem daty ręcznie. Powiedzmy, że mamy przedział od 2006-01-01 do 2007-05-31. Jeżeli będziemy mieli wpisaną datę 2006-06-01 i zaczniemy wpisywać datę 2007-01-01, która jest poprawna, i zaczniemy od podania roku to DTP nie przyjmie tej wartości, gdyż po ukończeniu wpisywania roku waliduje całą datę i stwierdzi, że data 2007-06-01 jest poza zakresem :/
W końcu po wielu zmaganiach stwierdziłem, że zdefiniowanie własnego zachowania poprzez dziedziczenie ze standardowego DTP ułatwi mi życie. Jak pomyślałem tak zrobiłem. Zdefiniowałem sobie nową właściwość z wybraną datą, do której mogę sobie bindować i być pewnym, że jeżeli walidacja nie przejdzie to nie pojawi mi się tam żadna dziwna data. Zdefiniowałem sobie swoje zdarzenie odpowiadające zmianie daty, które odpala się dopiero po faktycznym potwierdzeniu przez użytkownika wyboru daty. Dodatkowo pamiętam sobie datę sprzed edycji co pozwala mi ignorować sytuację, gdy użytkownik wybrał taką samą datę jaka była. Wszystko łatwo i przyjemnie i tak jak ja chcę.
 
Uważam, że pisząc jakikolwiek większy system powinniśmy korzystać z opakowanych wersji standardowych kontrolek. Nawet jeżeli to opakowanie nie wnosiłoby nic nowego to jednak w przyszłości pozwoli na łatwe modyfikacje. Jak by na to nie patrzeć byłaby to inwestycja na przyszłość, która nie tylko pozwala utrzymać spójność, ale także ułatwia wprowadzanie zmian.

Opublikowane 23 kwietnia 2007 11:27 przez nuwanda
Filed under:

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

# re: Przemyślenia dotyczące kontrolek Windows Forms

23 kwietnia 2007 13:30 by dario-g

hehe, skąd ja to znam :)

Tak samo tyczy się to kontrolek Webowych. Przykładem jest dodanie funkcjonalności 'Readonly' w standardowy sposób dla każdej kontrolki, czyli wyszarzenie i zablokowanie interakcji.

# re: Przemyślenia dotyczące kontrolek Windows Forms

23 kwietnia 2007 13:50 by ucel

Witamy w klubie uzytkownikow kontrolek WinForms :).

Przezywalem to samo, jak potrzebowalem sterowac linia podzialu w PropertyGrid, zerknij sobie do mojego artykulu w pierwszym numerze zine :).

# re: Przemyślenia dotyczące kontrolek Windows Forms

23 kwietnia 2007 14:31 by radzaw

wszystko co standardowe jest do bani ;) czasem wystarcza w zupelnosci, lub nawet znacznie przewyzsza funkcjonalnoscia to czego oczekujemy, a czasem brak jakiejs funkcjonalnosci.

tworzac takie standardowe elementy trzeba isc na kompromisy.

Zgadzam sie z Twoim zdaniem na temat tego, iz tworzac system nalezy w jakis sposob "opakowac" standardowe elementy, bo i tak ktos kiedys zechce cos w nich zmienic, a bo to sie komus nie podoba, a to chcialby aby mialo swiatelka i kogucik...

# re: Przemyślenia dotyczące kontrolek Windows Forms

24 kwietnia 2007 10:35 by ucel

Dobrze by jednakl bylo, gdyby te kontrolki byly rozszerzalne. Odnosze wrazenie, ze pol Frameworka jest zaprojektowana bez glowy, albo przynajmniej zasady jakimi sie kierowali programisci ustalajac widzialnosc poszczegolnych klas nie do konca byly sprecyzowane. Istotne dla rozszerzalnosci pola sa pochowane jako private (vide PropertyGrid), a klasy, ktore moglyby byc spokojnie zaimplementowane jako rozszerzalne siedza sobie jako internal (ale juz nie sealed).  [:(]

# re: Przemyślenia dotyczące kontrolek Windows Forms

24 kwietnia 2007 10:42 by nuwanda

#ucel: Zgodzę się z Tobą, kontrolki nie są zaprojektowane, aby je rozszerzać. Też musiałem hackować numerica, żeby ustawiać pozycję kursora, bo textbox jest zaszyty głęboko... :/

# re: Przemyślenia dotyczące kontrolek Windows Forms

24 kwietnia 2007 11:44 by mgrzeg

No coz - tekst Wojtka w zinie #2 o TypeWrapperze nie wzial sie znikad. Widze, ze grupa uzytkownikow tego narzedzia stale sie bedzie powiekszac :)))

Co o tym myślisz?

(wymagane) 
wymagane 
(wymagane) 

  
Wprowadź kod: (wymagane)