Spis treści
Nowy moduł
Stworzenie nowego widoku rozpoczniemy od utworzenia nowego modułu POS. W Visual Studio wybieramy nowy projekt WPF Custom Control Library. Ten typ projektu automatycznie wygeneruje nam niezbędną strukturę oraz pozwoli na szybkie dodawanie nowych elementów. Na początku nasz pusty projekt będzie składał się z folderu Themes, w którym będzie plik Generic.xaml oraz pliku CustomControl1.cs, które możemy usunąć.
Kolejnym krokiem będzie utworzenie klasy Module.cs, która będzie odpowiedzialna za rejestrację naszego modułu w aplikacji POS. Klasa ta musi dziedziczyć po klasie bazowej ModuleBase (Comarch.POS.Presentation.Core) oraz odpowiednio implementować metodę Initialize. Wewnątrz tej metody będziemy rejestrować własne serwisy, widoki oraz viewmodele, rejestrować przyciski w menu głównym POS, rejestrować własne kontrolki, zarządzać widocznością właściwości kontrolek w zarządzaniu interfejsem, wpinać się (rozszerzać) istniejące w systemie kontenery oraz datagridy.
Aby nasz przyszły widok był zarządzalny przez użytkownika w trakcie działania aplikacji POS, należy w folderze Themes dodać nowy element typu Resource Dictionary (prawym na folderze Themes, następnie z menu kontekstowego wybieramy kolejno: Add, Resource Dictionary…). Nadajemy mu nazwę ModernUI.xaml i zapisujemy. W pliku tym będziemy definiować domyślne właściwości dla zarządzalnych elementów interfejsu (widoków, kontrolek) – więcej w artykule Zarządzanie widokiem i jego elementami. Na koniec musimy jeszcze zarejestrować ten zasób. Robimy to w konstruktorze klasy Module, dopisując następującą linijkę:
LayoutService.RegisterResources(typeof(Module));
(LayoutService jest klasą statyczną znajdującą się w przestrzeni Comarch.POS.Presentation.Core.Services).
Utworzenie widoku
Tworzenie nowego widoku rozpoczniemy od utworzenia folderu Views, w którym będziemy przechowywać wszystkie widoki projektu, oraz folder ViewModels na viewmodele. Następnie w folderze Views dodajemy nowy element typu User Control (WPF), przykładowo o nazwie OrdersView.xaml.
Kolejnym krokiem jest zmiana typu UserControl na View z przestrzeni Comarch.POS.Presentation.Core.
<core:View x:Class="Comarch.POS.Presentation.Sales.Views.OrdersView" xmlns:core="clr-namespace:Comarch.POS.Presentation.Core;assembly=Comarch.POS.Presentation.Core"
W code-behind usuwamy dziedziczenie po UserControl i implementujemy wymagany interfejs View.
Właściwość Header to string wyświetlany w sekcji nagłówka aplikacji – ustawienie jej wymagane jest tylko dla widoków, które będą otwierane jako widoki podstawowe, ponieważ tylko one mają prezentację nagłówka. W pozostałych przypadkach (tj. widok modalny oraz komunikatu) wystarczy ustawić String.Empty. Istnieje również możliwość utworzenia własnego nagłówka sekcji z dowolną zawartością – więcej w Dodanie własnego nagłówka.
Właściwość HeaderLayoutId powinna natomiast zawierać unikalną nazwę dla identyfikatora layout id, niezbędnego do poprawnego działania zarządzania widokiem przez użytkownika POS. Jeżeli widok nie będzie zarządzalny to właściwość tę możemy ustawić jako String.Empty. Dla potrzeby tej dokumentacji ustalamy tutaj wartość „OrdersViewId”.
Konstruktor bazowy wymaga przekazania obiektu typu IViewModel, dlatego teraz przejdziemy do utworzenia viewmodelu. W folderze ViewModels najpierw tworzymy folder o nazwie Orders. Następnie wewnątrz niego dodajemy nową klasę OrdersViewModel oraz interfejs IOrdersViewModel. Interfejs powinien być publiczny i dziedziczyć po IViewModel. Klasa OrdersViewModel także powinna być publiczna oraz powinna implementować klasę bazową ViewModelBase oraz interfejs IOrdersViewModel.
Dziedzicząc po ViewModelBase będziemy musieli zaimplementować metodę IsTarget. Wywoływana jest podczas nawigacji i pozwala instancji viewmodelu stwierdzić czy to ona powinna zostać aktywowana czy też nie (więcej na ten temat w artykule Nawigacja między widokami) – na chwilę obecną definiujemy, aby zwracała true.
Dodatkowo, jeżeli widok ma być zarządzalny przez użytkownika POS, musimy dodać jeszcze jedną klasę, viewmodel o nazwie DesignOrdersViewModel, która będzie dziedziczyć po DesignViewModelBase oraz implementować interfejs IOrdersViewModel. Całościową strukturę przedstawia rysunek zamieszczony obok.
Teraz wracamy do code-behind klasy OrdersView (plik OrdersView.xaml.cs) i parametryzujemy konstruktor, tak aby przyjmował parametr typu IOrdersViewModel o nazwie viewModel (uwaga ta nazwa jest istotna i zawsze musi mieć taką właśnie nazwę!). Następnie uzupełniamy konstruktor bazowy przekazując mu naszą zmienną viewModel.
Ostatnim krokiem jest rejestracja naszego nowego widoku, która została opisana w rozdziale Rejestracja widoków do nawigacji oraz zarządzania wyglądem.
Gotowy szablon tworzenia modułu z pustym widokiem dostępny w przykładzie Prosty moduł z nowym pustym widokiem.
Dodanie własnego nagłówka
Dla każdego widoku podstawowego można ustawić tekst nagłówka, który będzie prezentowany w górnej części aplikacji POS. Domyślnie można tam umieścić tylko łańcuch znakowy zdefiniowany we właściwości Header na widoku. W przypadku zaistnienia potrzeby umieszczenia w nagłówku bardziej złożonej struktury, można utworzyć własny widok nagłówka (custom header).
Definiowanie własnego nagłówka rozpoczynamy od dodania do folderu z widokiem nowego elementu User Control (WPF). Dla zwiększeni czytelności element przyjmie nazwę naszego widoku z przyrostkiem Header (w przypadku widoku OrdersView będzie to nazwa OrdersViewHeader).
Ostatnią czynnością będzie powrót do code-behind naszego widoku OrdersView i ustawienie w konstruktorze właściwości CustomHeaderType na typ naszego customowego nagłówka. Widokowi nagłówka automatycznie zostanie przypisany DataContext wskazujący na ViewModel widoku. W tym przypadku będzie to OrdersViewModel. Dzięki temu w naszym nagłówku będziemy mogli używać bindingu bezpośrednio do właściwości na viewmodelu widoku.
public partial class OrdersView { public OrdersView(IOrdersViewModel viewModel) : base(viewModel) { CustomHeaderType = typeof (OrdersViewHeader); InitializeComponent(); } … …
Rejestracja widoków do nawigacji oraz zarządzania wyglądem
Po utworzeniu widoku (zaimplementowaniu odpowiednio logiki działania w view-modelu oraz UI w xaml-u), niezbędnym do działania naszego widoku krokiem jest jego rejestracja. Aby tego dokonać otwieramy klasę Module, utworzoną podczas tworzenia projektu (parz Nowy moduł). W metodzie Initialize dodajemy następujące linijki:
Register<IOrderViewModel, OrderViewModel>(); RegisterViews(new ViewStructure<OrdersView, DesignOrdersViewModel>("OrdersView", Resources.ResourceManager);
Pierwsza z nich rejestruje nasz viewmodel w kontenerze, dzięki czemu w konstruktorze widoku automatycznie zostanie wstrzyknięta instancja interfejsu IOrdersViewModel.
Druga metoda pozwala na zarejestrowanie widoku w trybie zarządzania jego interfejsem przez użytkownika POS w trakcie działania aplikacji. Dzięki temu użytkownik po wejściu w widok zarządzania interfejsem, na liście widoków zobaczy nasz widok. Będzie mógł go otworzyć do edycji i zmienić wygląd wszystkim tym elementom, które zdefiniujemy jako zarządzalne (więcej o tym jak to zrobić w artykule Zarządzanie widokiem i jego elementami). Metoda wymaga przekazania dwóch parametrów. Pierwszy z nich to nazwa klucza w zasobach, natomiast drugi to instancja menadżera zasobów. Parametry te pozwalają na prezentację nazwy widoku w drzewie widoków w zarządzaniu widokami aplikacji POS.
Alternatywnie, jeżeli nie chcemy rejestrować widoku w trybie zarządzania interfejsem, musimy skorzystać z metody RegisterForNavigation<TView >() zamiast RegisterViews.
RegisterForNavigation<OrdersView>();
Dodanie widoku do menu głównego
Każdy widok może zostać dodany do menu głównego w postaci kafelka (TileButton), w celu umożliwienie otwarcia widoku w trybie podstawowym. Dodając widok do menu głównego zostanie on również automatycznie dodany do wysuwalnego menu bocznego, które możemy wywołać z poziomu dowolnego widoku podstawowego.
Aby dodać wcześniej utworzony widok w postaci kafelka do menu należy w metodzie Initialize klasy Module zarejestrować widok za pomocą metody RegisterMenuTile<TView>, gdzie TView to nazwa klasy widoku. Parametrami wywołania są natomiast odpowiednio nazwa klucza w zasobach, menager zasobów. Opcjonalnie można również zdefiniować delegat canExecute. W przypadku zwrócenia false, kafel będzie wyszarzony i nieklikalny. Można także podać delegat do akcji, które mają zostać wykonane przed otwarciem widoku lub/oraz zdefiniować uprawnienia, jakie będą wymagane do otwarcia widoku wraz z kluczem w zasobach, gdzie będzie zapisany komunikat, który pojawi się w oknie modalnym podnoszenia uprawnień, w przypadku gdy uprawnienia nie będą spełnione (więcej o uprawnieniach Weryfikacja uprawnień). Dla naszego przykładu zarejestrujemy wcześniej utworzony widok OrdersView.
RegisterMenuTile<OrdersView>("OrdersView", Resources.ResourceManager);
Po zarejestrowaniu widoku w menu głównym, widok będzie można otworzyć za pomocą odpowiedniego kafelka tam widocznego. Domyślnie kafelek będzie wyglądał tak, jak został zdefiniowany w globalnej konfiguracji interfejsu POS. Aby zmienić jego właściwości wizualne, należy dodać odpowiednie wpisy w pliku ModernUI.xaml, znajdującym się w folderze Themes. Przykładowo, aby ustawić nowy domyślny kolor dla naszego nowego kafelka, otwierającego widok OrdersView, należy dodać następujący wpis:
<SolidColorBrush x:Key="OrdersView.Default.Background" Color="Red" />
Każdy klucz w MordernUI będzie miał następujący format:
[LayoutId lub Nazwa typu kontrolki].Default.[Nazwa właściwości]
Natomiast cała definicja:
<[Nazwa typu właściwości] x:Key=”[klucz wg. formatu powyżej]” [atrybuty]>[wartość]</[Nazwa typu właściwości>
Atrybuty oraz wartość nie jest obowiązkowa i zależy stricte od typu właściwości, z którą mamy do czynienia.
LayoutId dla kafelka widoku OrdersView jest definiowany w pierwszym parametrze metody RegisterMenuTile. W naszym przypadku LayoutId użyty w kluczu definiującym kolor kafelka to OrdersView, ponieważ taką wartość ustawiliśmy wcześniej wywołując metodę rejestrującą widok w postaci kafla w menu głównym. Właściwości, wspierane przez zarządzanie interfejsem, zależne są od typu kontrolki. W przypadku kafelków jest to kontrolka TileButton. Wykaz właściwości wspieranych dla poszczególnym kontrolek można znaleźć tutaj: Lista wspieranych właściwości
Wpięcie modułu do aplikacji POS
Ostatnim etapem po skompilowaniu tworzonego modułu jest jego uruchomienie w środowisku docelowym, czyli w aplikacji POS. W tym celu zbudowany moduł w postaci biblioteki (lub bibliotek) należy skopiować do katalogu instalacyjnego aplikacji POS. Następnie otworzyć do edycji plik POS.exe.config, odnaleźć w nim sekcję <modules> i dopisać nowy moduł na końcu tej sekcji, wg wzoru:
<module assemblyFile=”[nazwa_modulu].dll” moduleType=”[namespace_klasy_module].Module, [namespace_klasy_module]” moduleName=”[ nazwa_modulu]” />
W przypadku posiadania większej liczby bibliotek, rejestrujemy tylko te, które posiadają zaimplementowaną klasę Module.