Niniejszy post jest kontynuacją cyklu o Reactive Extensions dla .NET ([RX 1], [RX 2], [RX 3], [RX 4], [RX 5], [RX 6], [RX 7], [RX 8], [RX9], [RX10]) i pojawią się w nim przykłady wykorzystania Reactive Extensions.
W ramach przykładu pokazane zostanie okno aplikacji, która w pasku statusu będzie wyświetlać, położenie kursora myszki w oknie aplikacji oraz informacja, czy kursor znajduje się w lewej, czy prawej części okna. Niniejszy przykład zostanie wykonany z wykorzystaniem WPF, należy więc pamiętać, że oprócz standardowej biblioteki Reactive Extensions (System.Reactive.dll) należy również dodać do projektu bibliotekę dedykowaną dla WPF (System.Reactive.Windows.Threading.dll).
Wspomniane okno można przygotować w XAML'u w sposób następujący:
<Window x:Class="RXExample_WpfApplication.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525" Initialized="Window_Initialized"> <DockPanel> <StatusBar DockPanel.Dock="Bottom" > <Label x:Name="label_mousemove" /> <Label x:Name="label_LR" /> </StatusBar> <Grid x:Name="grid"></Grid> </DockPanel> </Window>
Niezbędnej inicjacji,w tym m. in. podłączenia się z wykorzystaniem Reactive Extension (Rx) do zdarzeń myszy oraz stworzenia odpowiednich subskrypcji, można dokonać w funkcji Window_Initialized.
Do zdarzenia związanego z ruchem myszą (MouseMove) podłączamy się jak w przedstawionym wcześniej przykładzie:
var mouseMove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>( h => this.MouseMove += h, h => this.MouseMove -= h );
Wyświetlenie pozycji jest proste i wystarczy dokonać subskrypcji:
mouseMove .ObserveOn( DispatcherScheduler.Instance ) .Subscribe( m => label_mousemove.Content = String.Format( "Mouse x={0},y={1}", m.EventArgs.GetPosition( this ).X, m.EventArgs.GetPosition( this ).Y ) );
Warto tutaj pamiętać o wskazaniu odpowiedniego planisty (ObserveOn( DispatcherScheduler.Instance )), który zaktualizuje nam naszą etykietę z właściwego wątku, obsługującego graficzny interfejs aplikacji.
Zauważmy, że przy powyższej konfiguracji obserwowalnej listy (strumienia danych związanego z ruchem myszy), powiadomienia otrzymujemy przy każdym najmniejszym ruchu myszą. Przy takim podejściu, w przypadku szybkiego ruchu myszą, podawane dane będą nieczytelne, a ich wyświetlanie może w znacznym stopniu pochłaniać cenne zasoby. Jak to rozwiązać? Otóż możemy być zainteresowani otrzymywaniem powiadomień o nowym położeniu kursora np. co 1s, wystarczy do tego wykorzystać funkcję Sample.
var mouseMove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>( h => this.MouseMove += h, h => this.MouseMove -= h ) .Sample( TimeSpan.FromSeconds( 1 ) );
Jeszcze ciekawszym rozwiązaniem może być informowanie o położeniu kursora, tylko w przypadku gdy kursor znajduje się w pewnym miejscu nieruchomo (funkcja Throttle):
var mouseMove = Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>( h => this.MouseMove += h, h => this.MouseMove -= h ) .Throttle( TimeSpan.FromMilliseconds( 100 ) );
Pomyślcie jak łatwo można w ten sposób zrobić aplikację obsługującą mapę, nad którą poruszamy kursorem myszki i gdy ten jest nieruchomy, to wtedy wyświetlamy na mapie dookoła kursora dodatkowe informacje (np. punkty POI).
Zauważmy, że powyższe rozwiązania są możliwe dzięki prostym złożeniom. Pokazywałem to już wcześniej w części poświęconej LINQ, czyli bierzemy jedną obserwowalną kolekcję, używamy funkcji rozszerzającej (np. filtru) i tworzymy w ten sposób nową obserwowalną kolekcję. Jeśli już o LINQ mowa, zobaczmy jak można LINQ wykorzystać bardziej w tradycyjny sposób. Dla przykładu umieścimy na pasku statusu informację, w jakiej części okna (lewej, czy prawej) znajduje się kursor:
double halfWidth = this.Width / 2; var polewo = from ev in mouseMove.ObserveOn( DispatcherScheduler.Instance ) where ev.EventArgs.GetPosition( this ).X < halfWidth select ev; polewo.ObserveOn( DispatcherScheduler.Instance ).Subscribe( m => label_LR.Content = "lewo" ); var poprawo = from ev in mouseMove.ObserveOn( DispatcherScheduler.Instance ) where ev.EventArgs.GetPosition( this ).X >= halfWidth select ev; poprawo.ObserveOn( DispatcherScheduler.Instance ).Subscribe( m => label_LR.Content = "prawo" );
W następnej części artykułu powrócimy do tego przykładu i rozszerzymy go przykład kompozycji zdarzeń.
Brak komentarzy:
Prześlij komentarz