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