Enhancing Usability Study Group 70-511 – temat 9 WPF
May 24, 2015
Enhancing Usability Study Group 70-511 – temat 9
WPF
Nazywam się Maciej Zbrzezny Pracuje w firmie CAS, w której jest architektem
oprogramowania i programistą, współtworzę tam oprogramowanie do komunikacji w rozproszonych systemach automatyki przemysłowej i integracji z systemami biznesowymi (rodzina oprogramowania: CommServer).
Tworzę oprogramowanie wykorzystujące platformę .NET(głównie C#) i standard OPC.
Autor bloga „Programowanie i Technologie” (http://maciej-progtech.blogspot.com/).
Posiadam certyfikaty MCTS z obszaru ASP.NET i Windows Forms Applications.
O mnie
Wykorzystanie wielowątkowości i przetwarzania asynchronicznego
Implementacja „Globalizacji” i „Lokalizacji” Integrowanie Windows Forms i WPF w jednej
aplikacji
Co w tej części?
Kod obsługi zdarzenia (np. kliknięcia przycisku) powinien być krótki (ma miejsce „zamrożenie”)
Co z dłuższymi operacjami? Co z interakcją z użytkownikiem
Ważna też jest informacja o postępie wykonania operacji, jej zakończeniu, jak również pozwolenie na anulowanie operacji
Wykorzystanie wielowątkowości i przetwarzania asynchronicznego
System.ComponentModel.BackgroundWorker jest komponentem wspierającym programistę w wykonywaniu długotrwałych operacji (jak np. ładowanie pliku, komunikacja z bazą danych itp…)
Realizuje on zadaną pracę w osobnym wątku, jednocześnie udostępniając mechanizmy informujące o jego stanie, postępie, zakończeniu.
Pozwala na anulowanie operacji. Jego mechanizmy informacyjne korzystają z
głównego wątku okna aplikacji, dzięki czemu mogą łatwo aktualizować interfejs użytkownika
BackgroundWorker – niech on wykona pracę
RunWorkerAsync – podstawowa metoda, która wywołana powoduje aktywację zdarzenia DoWork. Można ją wywołać bez parametrów lub z parametrem (Object) jako parametr inicjalizacyjny przekazywany do operacji, która ma być wykonana
DoWork – metoda z obsługą zdarzenia (private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)) , które wykonane jest w osobnym wątku, więc interfejs użytkownika pozwala na dalszą obsługę poleceń ze strony użytkownika. DoWorkEventArgs ma m.in. właściwość Argument przez którą można pobrać przekazany, przez RunWorkerAsync argument.
BackgroundWorker - podstawy
Informacja o zakończeniu operacji:◦ Zdarzenie: RunWorkerCompleted. Ma miejsce kiedy praca do wykonania
w tle skończyła się lub miał miejsce wyjątek. Szczegółowe informacje (w tym czy operacja była anulowana, jaki rezultat, itp…) dostępne przez przekazany obiekt klasy RunWorkerCompletedEventArgs.
Informacja o postępie:◦ Właściwość: WorkerReportsProgress. Wskazuje, czy BackgroundWorker
może informować o postępie.◦ Metoda: ReportProgress. Wyzwala zdarzenie ProgressChanged. Można
do niej przekazać int (postęp w %) i object (stan).◦ Zdarzenie: ProgressChanged. Ma miejsce gdy jest wywołana metoda
ReportProgress. Udostępnia informacje poprzez obiekt klasy ProgressChangedEventArgs.
Informacja o stanie:◦ Właściwość: IsBusy. Wskazuje, czy BackgroundWorker aktualnie
wykonuje jakąś pracę.◦ Właściwość: CancellationPending. Wskazuje, czy aplikacja żąda
anulowania pracy.
BackgroundWorker – funkcje informacyjne
Właściwość: WorkerSupportsCancellation. Wskazuje, czy BackgroundWorker wspiera asynchroniczne anulowanie pracy.
Metoda: CancelAsync. Wysyła żądanie anulowania pracy w tle.
Wykonywana operacja sprawdza, czy nie pojawiło się żądanie anulowania poprzez właściwość CancellationPending (klasy BackgroundWorker) i jeżeli została anulowana, to powinna ustawić właściwość Cancel klasy DoWorkEventArgs.
BackgroundWorker – anulowanie operacji
DEMO
Oprócz klasy BackgroundWorker można oczywiście wykorzystywać:◦ Delegacje (Delegates) – synchronicznie i
asynchronicznie◦ Wątki (Threads)◦ Zadania (Tasks) (tylko od .NET 4.0)◦ I wszystkie możliwości synchronizacyjne dostępne
w .NET (Monitor, Mutex, Semaphore, …) Trzeba jednak pamiętać, że kontrolki
Windows Forms, czy WPF można tylko aktualizować z poziomu wątku, który je wytworzył!
Gdy BackgroundWorker nie wystarcza.
Cross-thread operation not valid …
Każda kontrolka ma właściwość InvokeRequired, informującą o konieczności aktualizacji poprzez metodę BeginInvoke, która wykonuje przekazaną delegację w wątku obsługującym kontrolkę.
Dostęp do kontrolki z innego wątku (Windows Forms)
http://maciej-progtech.blogspot.com/2009/07/winforms-cross-thread-operation-not.html
Aktualizacja kontrolek dostępna z poziomu Dispatcher’a:◦ Invoke◦ BeginInvoke
Sprawdzanie konieczności:◦ CheckAccess
(return true/false)
◦ VerifyAccess (return void oraz exception)
Dostęp do kontrolki z innego wątku (WPF)
http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx
DEMO
Globalizacja i lokalizacja to różne procesy związane z internacjonalizacją aplikacji:
◦ Globalizacja związana jest z formatowaniem istniejących danych do formatów właściwych dla danych ustawień kulturowych.
◦ Lokalizacja związana jest z dostarczaniem właściwych danych dla ustawień kulturowych.
Przykład:◦ Globalizacja – W niektórych krajach część całkowita liczby
jest oddzielana od ułamkowej kropką, a w innych przecinkiem. Globalizacja odpowiedzialna jest w takim przypadku za odpowiednie wyświetlanie danych.
◦ Lokalizacja – Tytuł okienka, napisy na przyciskach często powinny być różne, dla różnych języków w których aplikacja jest dostępna.
„Globalizacja” i „Lokalizacja”
Kultura w .NET związana jest informacją dotyczącą kultury dla danego kraju lub regionu, w którym aplikacja jest użyta.
Kultura w .NET wybierana jest za pośrednictwem kodu 1,2 lub 3 elementowego (zwykle: język-region), np.:◦ uz-UZ-Cyrl specyfikuje język Uzbecki, w regionie
Uzbekistanu i alfabet cyrylicę Ustawiania kultury:
◦ Dla wątku: System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(„pl-PL");
◦ Dla interfejsu: System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(„pl-PL");
Kultura (Culture)
1. Ustawiamy właściwość Localizable okienka (Form) na True.
2. Projektujemy UI okna i tłumaczymy wszystkie elementy UI na języki, w których chcemy mieć zlokalizowaną aplikację.
3. Dodajemy elementy UI dla domyślnej kultury. Będą one wykorzystane, gdy wspierana kultura nie będzie wyspecyfikowana.
4. Ustawiamy właściwość Language okienka (Form) na kulturę, w której chcemy mieć zlokalizowane okno.
5. Dodajemy zlokalizowaną zawartość UI do okna.6. Powtarzamy kroki 4 i 5 dla każdego języka.7. Kompilujemy i budujemy aplikację.
Lokalizacja w Windows Forms
Lokalizacja w WPF jest dostępna poprzez satelickie assembly przygotowane dla konkretnych języków. Lokalizowane elementy aplikacji korzystają z zasobów assembly, które są ładowane automatycznie w zależności od aktualnej kultury UI.
Kiedy zlokalizowana aplikacja startuje, najpierw wyszukuje assembly z zasobami dla określonego języka i regionu. Jeśli nie można znaleźć właściwego assembly, wyszukiwane jest tylko dla właściwego języka. Jeśli również to assembly nie można znaleźć, wyszukiwany jest zestaw dla kultury neutralnej. Jeżeli również tego nie można znaleźć rzucany jest wyjątek.
Wyjątków związanych z lokalizacją można uniknąć, poprzez ustawienie atrybutu NeutralResourcesLanguage. Ten atrybut specyfikuje, którą kulturę należy użyć jeżeli nie ma dostępnej początkowo wymaganej. Atrybut można ustawić w następujący sposób: NeutralResourcesLanguage: [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
Lokalizacja w WPF - podstawy
1. Dodanie atrybutu kultury (UICulture) do pliku projektu i build projektu, by wygenerować katalogi dedykowane dla danej kultury.
2. Oznaczymy lokalizowalne elementy atrybutem Uid aby były rozróżniane unikalnie. Należy wykonać ten krok dla każdego pliku XAML w aplikacji.
◦ Uwaga: Lokalizowane właściwości mogą dotyczyć innych elementów niż tylko łańcuchy tekstowe. Mogą to być również kolory, układy kontrolek lub dowolnych innych elementów ważnych dla kultury.
3. Ekstrahujemy lokalizowaną zawartość z aplikacji przy użyciu specjalistycznego narzędzia.
4. Tłumaczymy lokalizowaną zawartość.5. Tworzymy podkatalogi, które będą przechowywać assembly dla
różnych kultur.6. Tworzymy assembly dla różnych kultur przy pomocy
specjalistycznego narzędzia.
Lokalizacja w WPF – kroki do wykonania
Należy dodać atrybut UICulture do projektu:1. Otwieramy plik projektu
(<ProjectName>.csproj dla C# lub <ProjectName>.vbproj dla Visual Basic) przy pomocy dowolnego edytora tekstu (np. Notepad’a).
2. Odnajdujemy element XML: <PropertyGroup>. Wewnątrz elementu dodajmy ustawienie domyślnej kultury: <UICulture>en-US</UICulture>
3. Zapisujemy plik i wykonujemy Build aplikacji.
Ustawienie kultury dla projektu
W pliku XAML dodajemy atrybut Uid do lokalizowanych elementów, np.: <Button x:Uid="Button_1" Margin="112,116,91,122„ Name="Button1">Button</Button>
Używamy msbuild, by zrobić to automatycznie: msbuild /t:updateuid myApplication.vbproj
Lokalizowane elementy powinny być oznaczone
W lokalizacji aplikacji pomoże nam narzędzie LocBaml (http://msdn.microsoft.com/en-us/library/ms771568%28v=VS.90%29.aspx)
Generujemy plik csv ze wszystkimi elementami do lokalizacji locbaml /parse en-US\myApplication.resources.dll
Tłumaczymy Tworzymy odpowiedni podkatalog (np. fr-CA) i
generujemy w nim assemlby: locbaml /generate en-US\myApplication.resources.dll /trans:myApplication.resources.FrenchCan.csv /cul:fr-CA /out:fr-CA
LocBaml i sprawy związane
LocBaml nie jest narzędziem wchodzącym w skład VS lub .NET SDK
Trzeba go osobno pobrać i skompilować Identyfikatory zasobów w pliku CSV mogą się
powtarzać. W związku z tym proces odwrotny czyli wygenerowanie dll'ki na podstawie pliku CSV się nie powiedzie.
Czasami VS sam generuje zlokalizowaną DLL’kę, wtedy nie można użyć wygenerowanej przez LocBaml i trzeba dwie połączyć linkerem (al.exe).
Aby zautomatyzować proces trzeba napisać skrypty
LocBaml i uwagi do niego
DEMO
Nie wszystkie kontrolki znane z Windows Forms są w aplikacjach WPF (brakuje np. MaskedTextBox lub PropertyGrid), choć w większości przypadków dostępne są odpowiedniki
Bez problemu można używać okien dialogowych z poziomu aplikacji WPF (np. OpenFileDialog).
Dostępny jest WindowsFormsHost w WPF Dostępna jest kontrolka ElementHost w
Windows Forms
Integrowanie Windows Forms i WPF w jednej aplikacji
WindowsFormsHost jest elementem (kontrolką) WPF pozwalającą na osadzenie pojedynczego elementu będącego kontrolką Windows Forms.
Hostowana kontrolka Windows Forms jest automatycznie dopasowywana do rozmiaru kontrolki nadrzędnej (WindowsFormsHost).
Można używać WindowsFormsHost do deklaratywnego (z poziomu XAML) tworzenia instancji kontrolek Windows Forms i również ustawiania ich właściwości w sposób deklaratywny.
WindowsFormsHost w WPF
Dodajemy namespaces:◦ xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
◦ xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
Dodajemy WindowsFormsHost i element z Windows Forms:
<my:WindowsFormsHost Margin="48,106,30,56" Name="windowsFormsHost1"> <wf:Button Text="Windows Forms Button" /></my:WindowsFormsHost> Możemy odczytać referencję od osadzonego elementu
Windows Forms poprzez właściwość Child, np.: System.Windows.Forms.Button aButton; aButton = (System.Windows.Forms.Button)windowsFormsHost1.Child;
WindowsFormsHost w WPF – w praktyce
DEMO
Kontrolka ElementHost w Windows Forms pozwala na osadzanie kontrolek WPF.
Kontrolka WPF jest podstawiana do właściwości Child klasy ElementHost.
WPF wewnątrz Windows Forms
WPFProject.UserControl1 aWPFcontrol = new WPFProject.UserControl1;ElementHost1.Child = aWPFcontrol;
DEMO
Dziękuję za uwagę