Menu - podejście nr2

Dlaczego podejście drugie? Ponieważ już raz pisałem o generowaniu Menu, ale trochę w inny sposób i w innym środowisku. Wtedy po prostu tworzyłem obiekty Xwt.MenuItem. Teraz zmieniłem podejście. Interfejs IMenuElement określa minimalny wspólny interfejs obiektów menu w dowolnym frameworku jakiego będę używał. I na podstawie definicji menu złożenej z obiektów IMenuElement będę generował odpowiednią strukturę obiektów.

WPF

Zacząłem od implementacji w WPF-ie. Wiedziałem, że praca w kodzie, bez XAMLa, może być troszkę skomplikowana, ale nie aż tak. Ilość dokumentacji jest minimalna, a wszystkie tutoriale odwołują się do XAMLa. Dopiero po jakiejś 1.5 godzinie szukania i próbowania do czegoś doszedłem.

Wygląda to tak, że MainWindow posiada własność Content, która domyślnie (co wynika z XAMLa) jest obiektem typu Grid. Do niego będę chciał włożyć obiekt typu Menu, który najpierw muszę stworzyć. Aby projekt miał przynajmniej ręce (a jak będzie miał nogi to jeszcze lepiej) zamiast generować menu w MainWindow, postanowiłem stworzyć nową klasę MenuBuilder, która przyjmuje IMenuProvider w konstruktorze. IMenuProvider dostarcza nam kolekcję obiektów IMenuElement dla menu najwyższego poziomu.

Jak wygląda MenuItem? Ma niezliczoną ilość własności i metod, co jest strasznie skomplikowane dla kogoś kto dopiero zabawę z WPF-em. Ale udało mi się dojść do kilku rzeczy:

  • Header – nagłówek, odpowiada IMenuElement.Label
  • IsCheckable & IsChecked– czy można zaznaczać i jeśli tak to czy jest zaznaczony, odpowiada IMenuElement.Checked
  • IsEnabled – czy da się kliknąć, odpowiada IMenuElement.Enabled
  • Icon – ikona przycisku, tylko nieliczni wybrańcy posiadający IconAttribute będą mieli ikonki z zestawu domyślnych
  • Items – dzieci obiektu, więc także elementy podmenu
  • Click – zdarzenie na wciśnięcie, odpowiada metodzie IMenuElement.Command(object, EventArgs)
  • Tag – dowolny obiekt pozwalający na identyfikację, wykorzystuję go do przechowywania obiektu IMenuElement, z którego MenuItem został wygenerowany.

Początkowo zamiast eventu Click chciałem utworzyć obiekt typu ICommand, co opisał Konrad Kokosa na swoim blogu (WPF oraz ICommand), ale w sumie to go nie potrzebowałem.

Więc mój MenuBuilder buduje całe drzewo menu, oraz udostępnia je we własności RootMenu. Teraz w MainWindow poproszę o to menu i dodam je do kolekcji Grida, o którym wspomniałem wyżej.

Efekt końcowy:

Efekt końcowy