Ostatnio miałem niemiłą zagwozdkę. Zostałem poproszony o napisanie własnej implementacji kontrolki, która ma zastąpić PageControl devexowy.

No przecież to proste… 4 przyciski, jakieś 2 text blocki i tyle. Wszystko spinam binduję do odpowiedniego ViewModelu i działa.

No nie 😀 Nie tak do końca… Zaciąłem się na implementacji ICommand z przycisku, który jest w kontrolce i obsłużeniu go moim view modelu.

Wcześniej robiłem już coś takiego, ale było to o tyle proste, że miałem z góry zdefiniowaną klasę view model i było to nie zmieniane.

DataContext kontrolki został spięty bezpośrednio z view modelem.

Definicja przycisku wyglądała następująco:

Po stronie ViewModelu obsługa RelayCommand

I koniec. Myślałem wcześniej, że zrobię to dokładnie w ten sam sposób ale nie przemyślałem tego, że moja kontrolka ma być uniwersalna i być niezależna od view modelu.

Czyli moje wcześniejsze rozwiązanie nie jest tym, o które mi chodzi.

Nie ukrywam, że trochę czasu poświęciłem na to aby w końcu problem rozwiązać w sposób zadowalający i wydaje mi się, że najbardziej optymalny 🙂

Schemat mojego projektu:

struktura_projektu

 Założenia do testowego projektu są następujące:

  • stworzyć kontrolkę użytkownika, która będzie zawierała przycisk i pole tekstowe
  • każdorazowe naciśnięcie przycisku będzie powodować to, że w polu tekstowym pokaże się ilość kliknięć w przycisk
  • projekt ma implementować wzorzec MVVM

Niby banał, ale najbardziej upierdliwe to binding kontrolek takich jak przycisk i text block z user control po stronie klasy view modelu 🙂

Stwórzmy sobie kontrolkę użytkownika w projekcie BindingTestControls

Dodajmy teraz trochę kodu w klasie kontrolki:

I tu właśnie zaczyna (jak ja to mówię) dziać się magia 🙂

Jako, że po dodaniu kontrolki do jakiegokolwiek widoku nie mamy dostępu do jej wewnętrznych kontrolek. Jak w naszym przypadku (przycisk i tekst) trzeba pomóc sobie inaczej 😉

W code behind kontrolki dodałem 2 dependence property. Jeden dla przycisku a drugi dla text block, które to będą widoczne w xaml po dodaniu naszej kontrolki do widoku.

W kodzie xaml kontrolki jest jedna bardzo ważna rzecz, na którą trzeba zwrócić uwagę

Przycisk sam w sobie ma właściwość Command, do której to możemy podpiąć nasz RelayCommand i obsłużyć naciśnięcie przycisku. I dokładnie to robimy. Tak samo dla właściwości Text kontrolki TextBlock. Z tą jedną różnicą, że ustawiamy RelativeSource dokładnie to tych dependence property, które zostały zdefiniowane w code behind. Po co ten cały zabieg? Ano po to, że właściwość Command na przycisku nie będzie widoczna w MainWindow.xaml (głównym widoku naszej aplikacji) ale możemy zrobić tak, że “wystawimy właściwość (w naszym przypadku) UserControlCommand, która to jest bezpośrednio spięta z Command naszego przycisku.

Przejdźmy teraz do głównego widoku naszej aplikacji

Jak można zauważyć nasza kontrolka posiada 2 właściwości:

  • UserControlCommand – ICommand dla obsługi przycisku
  • UserControlText – pole tekstowe kontrolki text block

Teraz wystarczy dodać klika linijek po stronie naszego view model:

I wszystko zaczyna działać dokładnie tak jakbyśmy tego chcieli 🙂

Kod oczywiście umieściłem na github dla potomnych.