Wyobraźmy sobie scenariusz….

Aplikacja spięta z bazą danych, na której pracuje wiele osób, (dzień jak co dzień 😀 kto takiego czegoś nie robił? :D) modyfikować dany rekord, wpis może tylko jedna osoba na raz. Przed rozpoczęciem wprowadzania danych leci do bazy zapytanie czy rekord można zablokować. Jeżeli tak to taki rekord w bazie zostaje zablokowany. Jeżeli dzieje się coś innego, jak na przykład, dostajemy informacje, że rekord został zmodyfikowany ale aktualnie klient nie posiada jego zmian to proszony jest o odświeżenie swojego widoku. A co jeżeli rekord jest zablokowany przez innego użytkownika a ktoś inny próbuje coś w nim zmienić?
Oczywiście baza odpowiada, że rekord jest zablokowany przez innego użytkownika i nic nie możemy w tym momencie z nim zrobić. Super!
Lecz patrząc na WPF, bindingi, zależności, mnogość innych rzeczy… Trzeba wymyślić coś co nie pozwoli w takiej sytuacji zmienić zablokowanego rekordu, czyli należy uniemożliwić użytkownikowi zmiany w formularzu/gridzie czy czymkolwiek innym.

Osobiście miałam takie wyzwanie jakiś czas temu, podejście do tego co zrobiliśmy z zespołem nie do końca mnie zadowalało ponieważ nie miało w sobie tego “czegoś”. Tworzyliśmy swoje własne kontrolki (DevExpress nas do tego zmuszał…) w nich setki własnych dependence property. Po stronie XAML’a wiele eventów odpowiadających za edycję danego elementu wywoływało jeden RelayCommand, który w switch sprawdzał przychodzący rodzaj eventu i w zależności od statusu rekordu pozwalał edytować formularz lub nie.
Szczerze…
Inne rozwiązanie chodziło mi po głowie wiele miesięcy starałem się zrobić coś co będzie w miarę ładne, proste i nie trzeba będzie pamiętać o tym, że przy dodaniu kontrolki X w nowym XAML należy jeszcze (w zależności co ma robić) zaimplementować te eventy, które w danym momencie są potrzebne. Kilka ładnych wieczorów spędziłem nad tym aby znaleźć takie rozwiązanie i dzisiaj w końcu osiągnąłem to co chciałem 🙂

Rozwiązanie okazało się banalnie proste a ja jak zwykle za bardzo kombinowałem 😀

Ale od początku.
Mamy formatkę z kilkoma kontrolkami. Jedną z nich jest ComboBox

Aby zabezpieczyć się przed zmianą danych w tej kontrolce należałoby sprawdzić chociażby te 3 eventy, które są widoczne (ale zawsze może być ich więcej niestety)

Po stronie ViewModelu trzeba zaimplementować RelayCommand, który rożnego rodzaju eventy sprawdzi i w zależności od tego czy formularz (w tym wypadku kontrolka ComboBox) może być edytowalna czy też nie.

Właściwość CanChanged symuluje pytanie do bazy i jej zwrotną informację. Rekord albo można edytować albo nie.

I teraz wyobraźmy sobie, że przeróżnych eventów będzie 30 razy więcej…. No u mnie było ich co najmniej 6, ale to DevEx….

Prawda, że nie wygląda to zachęcająco? 😉

Tak jak już wcześniej wspomniałem, kontrolki na formatkach były nasze, które dziedziczyły z innych kontrolek i tam właśnie brakowało mi rozwiązania, które pozwoli mi w łatwy sposób zarządzać blokowaniem wprowadzania danych.

Aby programowanie stało się “przyjemniejsze” wymyśliłem coś takiego.

Prosty interfejs, który ma implementować każda kontrolka. Mega prosty event do zaimplementowania, czyż nie? 😀

A teraz ta wisienka na torcie, czyli dlaczego tylko jeden…

Stworzyłem kontrolkę, która dziedziczy bo ComboBox WPF’owym i dodatkowo implementuje wcześniej pokazany interfejs.

Napisanie custom eventu nie graniczy z cudem ponieważ jest to jak budowa cepa 🙂
Najważniejsza w tym wszystkim jest metoda RaiseCustomEvent, która zwraca wartość logiczną true lub false i to ona właśnie robi całą pracę.

Przyglądnijmy się pierwszemu lepszemu eventowi, którego ta klasa nadpisuje
protected override void OnPreviewTextInput(TextCompositionEventArgs e) .
Parametrem, który dostajemy jest klasa TextCompositionEventArgs, która to w sobie ma property Handled, którego ustawienie na true powoduje, że event przestaje się wykonywać. I tutaj właśnie ukazuje się mój pomysł.
Stworzyłem jeden event, który posiada property Handled i to właśnie jego będę zawsze wywoływał w nadpisanych innych eventach, które mnie interesują. Metoda RaiseCustomEvent zwraca mi informacje o tym czy rekord może być edytowany czy nie.

Po zmodyfikowaniu mojego ViewModelu i RelayCommand otrzymuję coś takiego:

Jeden event obsługiwany po stronie ViewModelu, mniej niepotrzebnego kodu i jesteśmy bardziej SOLID 😀

Jak po zmianach wygląda nasza kontrolka w XAML?

Lepiej, prawda?
W tym momencie można jakikolwiek inny event, który chcemy zablokować, nadpisać w klasie kontrolki i będzie to ładniej wyglądać niż w przykładzie, który pokazałem na samym początku.

Kod do mojego rozwiązania jak zawsze na GitHub