# Klasa Context Dokumentacja klasy Context odpowiedzialnej za komunikację między warstwą logiki biznesowej a interfejsem graficznym. ## Czym jest Context? Context to **kontener par klucz-wartość**, gdzie: - **Klucz** = typ (Type) - **Wartość** = obiekt tego typu (object) Kontekst jest stale aktualizowany podczas pracy z programem i przechowuje informacje o aktualnym stanie interfejsu. ## Zawartość kontekstu Przykładowa zawartość przy otwartej liście kontrahentów: | Typ | Opis | |-----|------| | `SelectedCounter` | Liczba zaznaczonych wierszy na gridzie | | `Kontrahent[]` | Kolekcja zaznaczonych kontrahentów | | `UILocation` | Aktywny element interfejsu | | `INavigatorContext` | Kontekst grida (zaznaczenia, focus) | | `View` | Źródło danych grida | | `Params` | Klasa parametrów filtrów | | `Login` | Zalogowany użytkownik | ## Odczyt z kontekstu ### Metody GetOrDefault i GetRequired (zalecane) ```csharp public void Action(Context context) { // Zwraca obiekt lub null, gdy brak obiektu Kontrahent? knt = context.GetOrDefault(); // Zwraca obiekt lub wyjątek, gdy brak obiektu Kontrahent knt2 = context.GetRequired(); } ``` ### Przez indeksator ```csharp public void Action(Context cx) { // Rzuca wyjątek gdy brak obiektu w kontekście Kontrahent knt = (Kontrahent)cx[typeof(Kontrahent)]; // Bez wyjątku - drugi parametr Kontrahent knt2 = (Kontrahent)cx[typeof(Kontrahent), false]; } ``` ### Przez metodę Get (bezpieczna) ```csharp public void Action(Context cx) { // Zwraca true jeśli znaleziono, false jeśli nie if (cx.Get(out DokumentHandlowy dokument)) { // dokument znaleziony } else { // dokument nie znaleziony (dokument = null) } } ``` ## Zapis do kontekstu ```csharp public void Action(Context cx) { // Przez indeksator z określeniem typu Kontrahent knt = ...; cx[typeof(Kontrahent)] = knt; // Przez metodę Set DokumentHandlowy dok = ...; cx.Set(dok); } ``` ## Zastosowania ### 1. Filtry na listach głównych Klasy parametrów filtrów dziedziczą z `ContextBase` i są automatycznie w kontekście. ```csharp // Definicja klasy parametrów (w ViewInfo) public class TowaryParams(Context context) : ContextBase(context) { public Magazyn? Magazyn { get => Context.GetOrDefault(); set => Context.Set(value); } public TypTowaru Typ { get => Context.GetOrDefault(); set => Context.Set(value); } [Accessor(AutoChange = true)] public string SearchString { get; set; } } ``` ```xml ``` Wartości filtrów są dostępne przez kontekst dla: - Widoków (filtrowanie danych) - Workerów (właściwości wyliczane) - Wydruków ### 2. Wartości domyślne nowych obiektów Kontekst używany do inicjalizacji nowych obiektów wartościami z filtrów. Uzupełniane są właściwości zaznaczone atrybutem `[Context]`, który oznacza próbę odczytania wartości do ustawienia property z kontekstu. ``` Lista faktur: Filtr Magazyn: "Sklep" Filtr Kontrahent: "Drynda" ↓ Nowy dokument: Magazyn: "Sklep" (z kontekstu - property z [Context]) Kontrahent: "Drynda" (z kontekstu - property z [Context]) ``` ### 3. Wydruki Wydruki mają dostęp do obiektów z kontekstu jako źródła danych. ```csharp // W kodzie wydruku Context cx = ...; if (cx.Get(out Kontrahent[] kontrahenci)) { // kontrahenci[] = zaznaczone na liście } ``` ### 4. Workery - atrybut [Context] Workery mogą pobierać parametry z kontekstu automatycznie. ```csharp public class MojWorker { [Context] // Pobierane z kontekstu public Magazyn Magazyn { get; set; } [Context] // Jeśli brak w kontekście - okno parametrów public Kontrahent Kontrahent { get; set; } } ``` ### 5. Właściwości wyliczane zależne od filtrów ```csharp // Worker wyliczający stan magazynowy public class StanMagazynu : IWorker { public object Compute(Context cx, object source) { Towar towar = source as Towar; // Pobranie magazynu z kontekstu (z filtra) if (cx.Get(out Magazyn magazyn)) { return towar.GetStan(magazyn); } return towar.GetStanCalkowity(); } } ``` ## Klasa ContextBase Bazowa klasa dla obiektów automatycznie umieszczanych w kontekście. ```csharp public class MojaKlasaParametrow(Context context) : ContextBase(context) { [Accessor(AutoChange = true)] public Date DataOd { get; set; } [Accessor(AutoChange = true)] public Date DataDo { get; set; } public Kontrahent Kontrahent { get => Context.GetOrDefault(); set => Context.Set(value); } } ``` Obiekty dziedziczące z `ContextBase` i nie tylko: - Są automatycznie dodawane do kontekstu - Wywoływane jest zdarzenie `OnChanged` przy zmianie właściwości - Obsługują bindowanie do kontrolek UI - Właściwości połączone z UI (formularze, parametry) mogą być zadeklarowane z `[Accessor(AutoChange = true)]`, dzięki czemu Accessor automatycznie uruchomi mechanizm powiadamiania o zmianach i nie będzie konieczne wywołanie `Session.InvokeChanged()` lub `Context.InvokeChanged()` ## Interfejs INavigatorContext Dostępny w kontekście gdy aktywna jest lista (grid). ```csharp public void Action(Context cx) { if (cx.Get(out INavigatorContext nav)) { // Wiersz z focusem object current = nav.Current; // Zaznaczone wiersze IEnumerable selected = nav.Selected; // Liczba zaznaczonych int count = nav.SelectedCount; } } ``` ## Kolekcje zaznaczonych obiektów W kontekście znajdują się tablice zaznaczonych obiektów. ```csharp public void Action(Context cx) { // Zaznaczeni kontrahenci if (cx.Get(out Kontrahent[] kontrahenci)) { foreach (var k in kontrahenci) { // ... } } // Zaznaczone dokumenty if (cx.Get(out DokumentHandlowy[] dokumenty)) { // ... } } ``` ## Context implementuje ISessionable ```csharp public void Action(Context cx) { // Dostęp do sesji przez kontekst Session session = cx.Session; // Dostęp do modułu przez sesję var tm = session.GetTowary(); } ``` ## Pełny przykład - Worker z kontekstem ```csharp [assembly: Worker] namespace Soneta.Towary; public class TowarExtenderParams(Context context) : ContextBase(context) { [Accessor(AutoChange = true)] [Caption("Magazyn filtrowania")] public Magazyn MagazynFiltra { get; set; } } public class TowarExtender { [Context] public TowarExtenderParams Params { get; set; } [Context] public Towar Towar { get; set; } public decimal StanWMagazynie => Params.MagazynFiltra != null ? PoliczStanMagazynu(Towar, Params.MagazynFiltra) : PoliczStanMagazynu(Towar); private decimal PoliczStanMagazynu(Towar towar, Magazyn magazyn) { // Wyliczyć stan we wskazanym magazynie return 0; } private decimal PoliczStanMagazynu(Towar towar) { // Wyliczyć stan w całej firmie return 0; } } ``` ## Dobre praktyki 1. **Używaj Get, GetOrDefault, GetRequired** zamiast indeksatora - bezpieczniejsze 2. **Sprawdzaj obecność** obiektów w kontekście przed użyciem 3. **Dziedzicz z ContextBase** dla własnych klas parametrów i pamiętaj o konstruktorze `(Context context)` 4. **Używaj [Context]** w workerach dla parametrów z kontekstu 5. **Stosuj `[Accessor(AutoChange = true)]`** lub `InvokeChanged()` dla powiadamiania UI o zmianach ## Typowe typy w kontekście | Typ | Kiedy dostępny | |-----|----------------| | `Login` | Zawsze po zalogowaniu | | `Database` | Zawsze po zalogowaniu | | `LicencjaProgramu` | Zawsze po zalogowaniu | | `Session` | Gdy aktywny widok z danymi | | `View` | Gdy aktywna lista | | `INavigatorContext` | Gdy aktywna lista | | `[Obiekt][]` | Zaznaczenia na liście | | `[ViewInfo]+Params` | Klasa parametrów widoku | | `UILocation` | Lokalizacja w UI |