2. 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. maciejzbrzezny@gmail.com O mnie
3. Wykorzystanie wielowątkowości i przetwarzania asynchronicznego Implementacja „Globalizacji” i „Lokalizacji” Integrowanie Windows Forms i WPF w jednej aplikacji Co w tej części?
4. 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
5. System.ComponentModel.BackgroundWorkerjest 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ę
6. 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
7. 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 BackgroundWorkeraktualnie wykonuje jakąś pracę. Właściwość: CancellationPending. Wskazuje, czy aplikacja żąda anulowania pracy. BackgroundWorker – funkcje informacyjne
8. 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
10. 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.
12. 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
13. Dostęp do kontrolki z innego wątku (WPF) Aktualizacja kontrolek dostępna z poziomu Dispatcher’a: Invoke BeginInvoke Sprawdzanie konieczności: CheckAccess (return true/false) VerifyAccess (return void oraz exception) http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx
15. 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”
16. 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-Cyrlspecyfikuje język Uzbecki, w regionie Uzbekistanu i alfabet cyrylicę Ustawiania kultury: Dla wątku: System.Threading.Thread.CurrentThread.CurrentCulture = newSystem.Globalization.CultureInfo(„pl-PL"); Dla interfejsu: System.Threading.Thread.CurrentThread.CurrentUICulture = newSystem.Globalization.CultureInfo(„pl-PL"); Kultura (Culture)
17. Ustawiamy właściwośćLocalizable okienka (Form) naTrue. Projektujemy UI okna i tłumaczymy wszystkie elementy UI na języki, w których chcemy mieć zlokalizowaną aplikację. Dodajemy elementy UI dla domyślnej kultury. Będą one wykorzystane, gdy wspierana kultura nie będzie wyspecyfikowana. Ustawiamy właściwość Language okienka (Form) na kulturę, w której chcemy mieć zlokalizowane okno. Dodajemy zlokalizowaną zawartość UI do okna. Powtarzamy kroki 4 i5 dla każdego języka. Kompilujemy i budujemy aplikację. Lokalizacja w Windows Forms
18. 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 atrybutuNeutralResourcesLanguage. 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
19. Dodanie atrybutu kultury (UICulture)do pliku projektu i build projektu, by wygenerować katalogi dedykowane dla danej kultury. Oznaczymy lokalizowalne elementy atrybutemUidaby 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. Ekstrahujemy lokalizowaną zawartość z aplikacji przy użyciu specjalistycznego narzędzia. Tłumaczymy lokalizowaną zawartość. Tworzymy podkatalogi, które będą przechowywać assembly dla różnych kultur. Tworzymy assemblydla różnych kultur przy pomocy specjalistycznego narzędzia. Lokalizacja w WPF – kroki do wykonania
20. Należy dodać atrybut UICulture do projektu: Otwieramy plik projektu (<ProjectName>.csprojdlaC# lub <ProjectName>.vbprojdlaVisual Basic) przy pomocy dowolnego edytora tekstu (np. Notepad’a). Odnajdujemy element XML: <PropertyGroup>. Wewnątrz elementu dodajmy ustawienie domyślnej kultury: <UICulture>en-US</UICulture> Zapisujemy plik i wykonujemy Build aplikacji. Ustawienie kultury dla projektu
21. 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:updateuidmyApplication.vbproj Lokalizowane elementy powinny być oznaczone
22. 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-USyApplication.resources.dll Tłumaczymy Tworzymy odpowiedni podkatalog (np. fr-CA) i generujemy w nim assemlby: locbaml /generateen-USyApplication.resources.dll /trans:myApplication.resources.FrenchCan.csv /cul:fr-CA /out:fr-CA LocBaml i sprawy związane
23. 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
25. Nie wszystkie kontrolki znane z Windows Forms są w aplikacjach WPF (brakuje np. MaskedTextBoxlub 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 ElementHostw Windows Forms Integrowanie Windows Forms i WPF w jednej aplikacji
26. WindowsFormsHostjest 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ć WindowsFormsHostdo 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
27. WindowsFormsHost w WPF – w praktyce 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.ButtonaButton; aButton= (System.Windows.Forms.Button)windowsFormsHost1.Child;
29. 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;
The difference between CheckAccess and VerifyAccess is CheckAccess returns a Boolean if the calling thread does not have access to the Dispatcher and VerifyAccess throws an exception.DispatcherPriorityEnumerationduża tabelka dostępna http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcherpriority.aspx// Uses the Dispatcher.CheckAccess method to determine if // the calling thread has access to the thread the UI object is on.privatevoidTryToUpdateButtonCheckAccess(objectuiObject){ Button theButton = uiObject as Button;if (theButton != null) { // Checking if this thread has access to the object.if (theButton.Dispatcher.CheckAccess()) { // This thread has access so it can update the UI thread.UpdateButtonUI(theButton); }else { // This thread does not have access to the UI thread. // Place the update method on the Dispatcher of the UI thread.theButton.Dispatcher.BeginInvoke(DispatcherPriority.Normal,newUpdateUIDelegate(UpdateButtonUI), theButton); } }}
Zwróćmy uwagę że właściwości są ustawiane deklaratywnie, tak samo jak w WPF