Omówienie danych wejściowych
podsystem Windows Presentation Foundation (WPF) udostępnia zaawansowany interfejs API do uzyskiwania danych wejściowych z różnych urządzeń, w tym myszy, klawiatury, dotyku i rysika. W tym temacie opisano usługi udostępniane przez WPF i objaśniono architekturę systemów wejściowych.
Interfejs API wejściowy
Podstawowa ekspozycja interfejsu API danych wejściowych znajduje się w klasach elementów podstawowych: UIElement, ContentElement, FrameworkElementi FrameworkContentElement. Aby uzyskać więcej informacji na temat elementów podstawowych, zobacz Podstawowe elementy — omówienie. Klasy te zapewniają funkcjonalność dla zdarzeń wejściowych związanych z naciśnięciami klawiszy, przyciskami myszy, kółkiem myszy, ruchem myszy, zarządzaniem fokusem i przechwytywaniem myszy, aby wymienić kilka. Umieszczając wejściowy interfejs API w elementach podstawowych, a nie traktując wszystkich zdarzeń wejściowych jako usługi, architektura danych wejściowych umożliwia źródło zdarzeń wejściowych przez określony obiekt w interfejsie użytkownika oraz obsługę schematu routingu zdarzeń, w którym więcej niż jeden element ma możliwość obsługi zdarzenia wejściowego. Wiele zdarzeń wejściowych ma skojarzoną parę zdarzeń. Na przykład zdarzenie wyłączania klucza jest skojarzone z zdarzeniami KeyDown i PreviewKeyDown. Różnica w tych zdarzeniach polega na tym, jak są one kierowane do elementu docelowego. Zdarzenia przeglądowe przechodzą w dół drzewa elementów, od elementu głównego do elementu docelowego. Zdarzenia bubbling przemieszczają się od elementu docelowego do elementu głównego. Routing zdarzeń w WPF został omówiony bardziej szczegółowo w dalszej części tego przeglądu i w Przegląd zdarzeń trasowanych.
Klasy klawiatury i myszy
Oprócz API wejścia w podstawowych klasach elementów, klasy Keyboard i Mouse zapewniają dodatkowe API do pracy z klawiaturą i obsługą myszy.
Przykłady wejściowego interfejsu API w klasie Keyboard to właściwość Modifiers, która zwraca aktualnie naciśnięty ModifierKeys, oraz metoda IsKeyDown, która określa, czy dany klawisz jest naciśnięty.
W poniższym przykładzie użyto metody GetKeyStates, aby określić, czy Key jest w stanie wyłączonym.
// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison.
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
btnNone.Background = Brushes.Red;
}
' Uses the Keyboard.GetKeyStates to determine if a key is down.
' A bitwise AND operation is used in the comparison.
' e is an instance of KeyEventArgs.
If (Keyboard.GetKeyStates(Key.Return) And KeyStates.Down) > 0 Then
btnNone.Background = Brushes.Red
Przykłady wejściowego interfejsu API w klasie Mouse to MiddleButton, który uzyskuje stan środkowego przycisku myszy, a DirectlyOver, który pobiera element, nad którym wskaźnik myszy znajduje się obecnie.
Poniższy przykład określa, czy LeftButton myszy znajduje się w stanie Pressed.
if (Mouse.LeftButton == MouseButtonState.Pressed)
{
UpdateSampleResults("Left Button Pressed");
}
If Mouse.LeftButton = MouseButtonState.Pressed Then
UpdateSampleResults("Left Button Pressed")
End If
Klasy Mouse i Keyboard zostały szczegółowo omówione w tym omówieniu.
Wejście rysika
WPF ma zintegrowaną obsługę Stylus. Stylus to wejście pióra, spopularyzowane przez komputer tablet. Aplikacje WPF mogą traktować rysik jako mysz przy użyciu API myszy, ale WPF uwidacznia również abstrakcję urządzenia rysika, który działa w modelu podobnym do klawiatury i myszy. Wszystkie API związane z rysikiem zawierają słowo "Stylus".
Ponieważ rysik może działać jak myszka, aplikacje, które obsługują tylko wprowadzanie myszy, nadal mogą uzyskać pewien poziom obsługi rysika automatycznie. Gdy rysik jest używany w taki sposób, aplikacja ma możliwość obsługi odpowiedniego zdarzenia rysika, a następnie obsługuje odpowiednie zdarzenie myszy. Ponadto usługi wyższego poziomu, takie jak wprowadzanie atramentu, są również dostępne poprzez abstrakcję urządzenia rysika. Aby uzyskać więcej informacji na temat pisma odręcznego jako danych wejściowych, zobacz Wprowadzenie do pisma odręcznego.
Routing zdarzeń
FrameworkElement może zawierać inne elementy jako elementy podrzędne w swoim modelu zawartości, tworząc drzewo elementów. W WPF element nadrzędny może uczestniczyć w obsłudze danych wejściowych skierowanych do jego elementów podrzędnych lub innych potomków poprzez obsługę zdarzeń. Jest to szczególnie przydatne w przypadku tworzenia kontrolek z mniejszych kontrolek, procesu znanego jako "kompozycja sterowania" lub "komponowanie". Aby uzyskać więcej informacji o drzewach elementów i sposobie, w jaki drzewa elementów odnoszą się do tras zdarzeń, zobacz Trees in WPF.
Routing zdarzeń to proces przekazywania zdarzeń do wielu elementów, dzięki czemu określony obiekt lub element wzdłuż trasy może wybrać zaoferowanie znaczącej odpowiedzi (poprzez obsługę) zdarzenia, które mogło pochodzić od innego elementu. Zdarzenia trasowane używają jednego z trzech mechanizmów routingu: bezpośredniego, bąbelkowania oraz tunelowania. W routingu bezpośrednim element źródłowy jest jedynym elementem powiadamianym, a zdarzenie nie jest kierowane do żadnych innych elementów. Jednak bezpośrednio kierowane zdarzenie nadal oferuje pewne dodatkowe możliwości, które są dostępne wyłącznie dla zdarzeń kierowanych, w odróżnieniu od standardowych zdarzeń CLR. Bubbling działa w drzewie elementów, najpierw powiadamiając element, który wygenerował zdarzenie, a następnie element nadrzędny itd. Tunelowanie rozpoczyna się od korzenia drzewa elementów i przesuwa się w dół, kończąc na oryginalnym elemencie źródłowym. Aby uzyskać więcej informacji na temat zdarzeń kierowanych, zobacz również Przegląd zdarzeń kierowanych.
Zdarzenia wejściowe WPF zazwyczaj występują w parach składających się z zdarzenia tunelowania i zdarzenia bubbling. Zdarzenia tunelowania różnią się od zdarzeń bubbling z prefiksem "Wersja zapoznawcza". Na przykład PreviewMouseMove jest tunelowaną wersją zdarzenia przenoszenia myszy, a MouseMove jest propagującą wersją tego zdarzenia. To parowanie zdarzeń jest konwencją zaimplementowaną na poziomie elementu i nie jest nieodłączną możliwością systemu zdarzeń WPF. Aby uzyskać więcej szczegółowych informacji, zobacz sekcję Zdarzenia wejściowe WPF w Przegląd zdarzeń trasowanych.
Obsługa zdarzeń wejściowych
Aby odbierać dane wejściowe dla elementu, program obsługi zdarzeń musi być skojarzony z tym konkretnym zdarzeniem. W języku XAML jest to proste: odwołujesz się do nazwy zdarzenia jako atrybutu elementu, który będzie nasłuchiwać tego zdarzenia. Następnie ustawiasz wartość atrybutu na nazwę procedury obsługi zdarzeń, którą zdefiniujesz, korzystając z delegata. Procedura obsługi zdarzenia musi być napisana w kodzie, takim jak C#, i może być zawarta w pliku zaplecza.
Zdarzenia klawiatury występują, gdy system operacyjny zgłasza akcje, które występują, gdy fokus klawiatury dotyczy elementu. Zdarzenia myszy i rysika są podzielone na dwie kategorie: zdarzenia, które zgłaszają zmiany położenia wskaźnika względem elementu, oraz zdarzenia, które zgłaszają zmiany stanu przycisków urządzenia.
Przykład zdarzenia wprowadzania klawiatury
Poniższy przykład reaguje na naciśnięcie strzałki w lewo. Jest utworzony StackPanel, który ma Button. Procedura obsługi zdarzeń do nasłuchiwania naciśnięcia strzałki w lewo jest dołączona do wystąpienia Button.
Pierwsza sekcja przykładu tworzy StackPanel oraz Button i dołącza procedurę obsługi zdarzeń dla KeyDown.
<StackPanel>
<Button Background="AliceBlue"
KeyDown="OnButtonKeyDown"
Content="Button1"/>
</StackPanel>
// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();
// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";
// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);
// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);
' Create the UI elements.
Dim keyboardStackPanel As New StackPanel()
Dim keyboardButton1 As New Button()
' Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue
keyboardButton1.Content = "Button 1"
' Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1)
' Attach event handler.
AddHandler keyboardButton1.KeyDown, AddressOf OnButtonKeyDown
Druga sekcja jest napisana w kodzie i definiuje procedurę obsługi zdarzeń. Gdy naciśnięta zostanie strzałka w lewo i Button ma fokus klawiatury, uruchamiany jest program obsługujący, a kolor Background na Button jest zmieniany. Jeśli klawisz jest naciśnięty, ale nie jest to klawisz strzałki w lewo, kolor Background elementu Button zostanie zmieniony z powrotem na jego kolor początkowy.
private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
Button source = e.Source as Button;
if (source != null)
{
if (e.Key == Key.Left)
{
source.Background = Brushes.LemonChiffon;
}
else
{
source.Background = Brushes.AliceBlue;
}
}
}
Private Sub OnButtonKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
Dim source As Button = TryCast(e.Source, Button)
If source IsNot Nothing Then
If e.Key = Key.Left Then
source.Background = Brushes.LemonChiffon
Else
source.Background = Brushes.AliceBlue
End If
End If
End Sub
Przykład zdarzenia wprowadzania myszy
W poniższym przykładzie kolor BackgroundButton jest zmieniany, gdy wskaźnik myszy wchodzi do Button. Kolor Background jest przywracany, gdy mysz opuszcza Button.
Pierwsza sekcja przykładu tworzy kontrolkę StackPanel i kontrolkę Button oraz dołącza programy obsługi dla zdarzeń MouseEnter i MouseLeave do Button.
<StackPanel>
<Button Background="AliceBlue"
MouseEnter="OnMouseExampleMouseEnter"
MouseLeave="OnMosueExampleMouseLeave">Button
</Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();
// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";
// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);
// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);
' Create the UI elements.
Dim mouseMoveStackPanel As New StackPanel()
Dim mouseMoveButton As New Button()
' Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue
mouseMoveButton.Content = "Button"
' Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton)
' Attach event handler.
AddHandler mouseMoveButton.MouseEnter, AddressOf OnMouseExampleMouseEnter
AddHandler mouseMoveButton.MouseLeave, AddressOf OnMosueExampleMouseLeave
Druga sekcja przykładu jest napisana w kodzie i definiuje procedury obsługi zdarzeń. Gdy kursor myszy znajdzie się nad Button, kolor Background dla Button zostanie zmieniony na SlateGray. Gdy mysz opuszcza Button, kolor Background obiektu Button zostaje przywrócony do AliceBlue.
private void OnMouseExampleMouseEnter(object sender, MouseEventArgs e)
{
// Cast the source of the event to a Button.
Button source = e.Source as Button;
// If source is a Button.
if (source != null)
{
source.Background = Brushes.SlateGray;
}
}
Private Sub OnMouseExampleMouseEnter(ByVal sender As Object, ByVal e As MouseEventArgs)
' Cast the source of the event to a Button.
Dim source As Button = TryCast(e.Source, Button)
' If source is a Button.
If source IsNot Nothing Then
source.Background = Brushes.SlateGray
End If
End Sub
private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
// Cast the source of the event to a Button.
Button source = e.Source as Button;
// If source is a Button.
if (source != null)
{
source.Background = Brushes.AliceBlue;
}
}
Private Sub OnMosueExampleMouseLeave(ByVal sender As Object, ByVal e As MouseEventArgs)
' Cast the source of the event to a Button.
Dim source As Button = TryCast(e.Source, Button)
' If source is a Button.
If source IsNot Nothing Then
source.Background = Brushes.AliceBlue
End If
End Sub
Wprowadzanie tekstu
Zdarzenie TextInput umożliwia nasłuchiwanie wprowadzania tekstu w sposób niezależny od urządzenia. Klawiatura jest podstawowym sposobem wprowadzania tekstu, ale mowa, pismo ręczne i inne urządzenia wejściowe mogą również generować tekst.
W przypadku danych wejściowych za pomocą klawiatury WPF najpierw wysyła odpowiednie zdarzenia KeyDown/KeyUp. Jeśli te zdarzenia nie są obsługiwane, a klucz jest tekstowy (zamiast klucza sterującego, takiego jak strzałki kierunkowe lub klucze funkcji), zostanie zgłoszone zdarzenie TextInput. Nie zawsze istnieje proste mapowanie jeden do jednego między zdarzeniami KeyDown,/,KeyUp a TextInput, ponieważ wiele naciśnięć klawiszy może wygenerować pojedynczy znak tekstu, a pojedyncze naciśnięcia mogą generować ciągi wieloznakowe. Dotyczy to szczególnie języków, takich jak chiński, japoński i koreański, które używają edytorów metod wejściowych (IME) do generowania tysięcy możliwych znaków w odpowiednich alfabetach.
Gdy WPF wysyła zdarzenie KeyUp/KeyDown, Key jest ustawiona na Key.System, jeśli naciśnięcia klawiszy mogą stać się częścią zdarzenia TextInput (jeśli na przykład ALT+S jest naciśnięty). Dzięki temu kod w procedurze obsługi zdarzeń KeyDown może sprawdzić, czy Key.System jest obecny i, jeśli zostanie znaleziony, pozostawić przetwarzanie dla procedury obsługi następnie zgłoszonego zdarzenia TextInput. W takich przypadkach różne właściwości argumentu TextCompositionEventArgs mogą służyć do określania oryginalnych naciśnięć. Podobnie, jeśli protokół IME jest aktywny, Key ma wartość Key.ImeProcessed, a ImeProcessedKey daje oryginalne naciśnięcie lub naciśnięcia.
W poniższym przykładzie zdefiniowano procedurę obsługi dla zdarzenia Click i procedury obsługi dla zdarzenia KeyDown.
Pierwszy segment kodu lub znaczników tworzy interfejs użytkownika.
<StackPanel KeyDown="OnTextInputKeyDown">
<Button Click="OnTextInputButtonClick"
Content="Open" />
<TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";
// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);
// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);
' Create the UI elements.
Dim textInputStackPanel As New StackPanel()
Dim textInputeButton As New Button()
Dim textInputTextBox As New TextBox()
textInputeButton.Content = "Open"
' Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton)
textInputStackPanel.Children.Add(textInputTextBox)
' Attach event handlers.
AddHandler textInputStackPanel.KeyDown, AddressOf OnTextInputKeyDown
AddHandler textInputeButton.Click, AddressOf OnTextInputButtonClick
Drugi segment kodu zawiera programy obsługi zdarzeń.
private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
{
handle();
e.Handled = true;
}
}
private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
handle();
e.Handled = true;
}
public void handle()
{
MessageBox.Show("Pretend this opens a file");
}
Private Sub OnTextInputKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
If e.Key = Key.O AndAlso Keyboard.Modifiers = ModifierKeys.Control Then
handle()
e.Handled = True
End If
End Sub
Private Sub OnTextInputButtonClick(ByVal sender As Object, ByVal e As RoutedEventArgs)
handle()
e.Handled = True
End Sub
Public Sub handle()
MessageBox.Show("Pretend this opens a file")
End Sub
Ponieważ zdarzenia wejściowe przepływają w górę ścieżki zdarzeń, StackPanel odbiera sygnały wejściowe niezależnie od tego, który element jest w fokusie klawiatury. Kontrolka TextBox jest najpierw powiadamiana, a program obsługi OnTextInputKeyDown
jest wywoływany tylko wtedy, gdy TextBox nie obsłużyła danych wejściowych. Jeśli zdarzenie PreviewKeyDown jest używane zamiast zdarzenia KeyDown, program obsługi OnTextInputKeyDown
jest wywoływany jako pierwszy.
W tym przykładzie logika obsługi jest zapisywana dwa razy — raz dla ctrl+O i ponownie dla zdarzenia kliknięcia przycisku. Można to uprościć za pomocą poleceń zamiast bezpośrednio obsługiwać zdarzenia wejściowe. Polecenia są omówione w tym omówieniu i w Omówienie poleceń.
Dotyk i manipulowanie
Nowy sprzęt i interfejs API w systemie operacyjnym Windows 7 zapewniają aplikacjom możliwość odbierania danych wejściowych z wielu dotknięciów jednocześnie. WPF umożliwia aplikacjom wykrywanie i reagowanie na dotyk w sposób podobny do reagowania na inne dane wejściowe, takie jak mysz lub klawiatura, przez wywoływanie zdarzeń w przypadku wystąpienia dotyku.
WPF uwidacznia dwa typy zdarzeń w przypadku wystąpienia dotyku: zdarzenia dotykowe i zdarzenia manipulowania. Zdarzenia dotykowe zapewniają nieprzetworzone dane dotyczące każdego palca na ekranie dotykowym i jego ruchu. Zdarzenia manipulowania interpretują dane wejściowe jako określone akcje. Oba typy zdarzeń zostały omówione w tej sekcji.
Warunki wstępne
Potrzebne są następujące składniki, aby utworzyć aplikację, która reaguje na dotyk.
Visual Studio 2010.
Windows 7.
Urządzenie, takie jak ekran dotykowy, który obsługuje funkcję Windows Touch.
Terminologia
Podczas omawiania dotyku są używane następujące terminy.
Touch jest typem danych wejściowych użytkownika rozpoznawanym przez system Windows 7. Zazwyczaj dotyk jest inicjowany przez umieszczenie palców na ekranie dotykowym. Należy pamiętać, że urządzenia takie jak touchpad, który jest typowy na komputerach przenośnych, nie obsługują dotyku, jeśli urządzenie tylko konwertuje położenie i ruch palca jako wejście myszy.
multitouch to dotyk występujący z więcej niż jednego punktu jednocześnie. Systemy Windows 7 i WPF obsługują funkcję multitouch. Za każdym razem, gdy dotyk jest omówiony w dokumentacji platformy WPF, koncepcje mają zastosowanie do wielodotyku.
manipulowania występuje, gdy dotyk jest interpretowany jako akcja fizyczna zastosowana do obiektu. W WPF zdarzenia manipulowania interpretują dane wejściowe jako translacja, rozszerzanie lub manipulowanie rotacją.
touch device
reprezentuje urządzenie, które produkuje wejście dotykowe, takie jak pojedynczy palec na ekranie dotykowym.
Kontrolki reagujące na dotyk
Poniższe kontrolki można przewijać, przesuwając palcem po kontrolce, jeśli zawiera treści, które zostały przewinięte poza widok.
ScrollViewer definiuje właściwość dołączoną ScrollViewer.PanningMode, która umożliwia określenie, czy przesuwanie dotykowe jest włączone w poziomie, w pionie, w obu lub nie. Właściwość ScrollViewer.PanningDeceleration określa, jak szybko przewijanie spowalnia, gdy użytkownik podnosi palec z ekranu dotykowego. Właściwość ScrollViewer.PanningRatio dołączona określa stosunek przesunięcia przewijania do przesunięcia manipulowania.
Zdarzenia dotykowe
Klasy podstawowe, UIElement, UIElement3Di ContentElement, definiują zdarzenia, które można subskrybować, aby aplikacja reagowała na dotyk. Zdarzenia dotykowe są przydatne, gdy aplikacja interpretuje dotyk jako coś innego niż manipulowanie obiektem. Na przykład aplikacja, która umożliwia użytkownikowi rysowanie za pomocą co najmniej jednego palca, będzie subskrybować zdarzenia dotykowe.
Wszystkie trzy klasy definiują następujące zdarzenia, które zachowują się podobnie, niezależnie od klasy definiującej.
Podobnie jak zdarzenia klawiatury i myszy, zdarzenia dotykowe są zdarzeniami kierowanymi. Zdarzenia rozpoczynające się od Preview
to zdarzenia tunelowania, a zdarzenia rozpoczynające się od Touch
są zdarzeniami bubbling. Aby uzyskać więcej informacji na temat zdarzeń kierowanych, zapoznaj się z Przegląd zdarzeń routowanych. W przypadku obsługi tych zdarzeń można uzyskać pozycję danych wejściowych względem dowolnego elementu, wywołując metodę GetTouchPoint lub GetIntermediateTouchPoints.
Aby zrozumieć interakcję między zdarzeniami dotykowymi, rozważ scenariusz, w którym użytkownik umieszcza jeden palec na elemecie, przenosi palcem w element, a następnie podnosi palec z elementu. Na poniższej ilustracji przedstawiono wykonywanie zdarzeń bąbelkowania (zdarzenia tunelowania są pomijane dla uproszczenia).
Zdarzenia dotykowe
Poniższa lista zawiera opis sekwencji zdarzeń na poprzedniej ilustracji.
Zdarzenie TouchEnter występuje jeden raz, gdy użytkownik umieści palec na elemecie.
Zdarzenie TouchDown występuje jednorazowo.
Zdarzenie TouchMove występuje wiele razy, gdy użytkownik przesuwa palec w elemecie .
Zdarzenie TouchUp występuje jeden raz, gdy użytkownik podnosi palec z elementu.
Zdarzenie TouchLeave występuje jednorazowo.
Gdy używane są więcej niż dwa palce, zdarzenia występują dla każdego palca.
Zdarzenia manipulowania
W przypadkach, gdy aplikacja umożliwia użytkownikowi manipulowanie obiektem, klasa UIElement definiuje zdarzenia manipulowania. W przeciwieństwie do zdarzeń dotykowych, które po prostu zgłaszają położenie dotyku, zdarzenia manipulacji raportują sposób interpretowania danych wejściowych. Istnieją trzy typy manipulacji, tłumaczenia, rozszerzania i rotacji. Na poniższej liście opisano sposób wywoływania trzech typów manipulacji.
Umieść palec na obiekcie i przesuń palcem po ekranie dotykowym, aby wywołać manipulację translacji. Zwykle powoduje to przeniesienie obiektu.
Umieść dwa palce na obiekcie i przesuń je bliżej siebie lub dalej od siebie, aby zainicjować powiększenie. Zwykle zmienia to rozmiar obiektu.
Umieść dwa palce na obiekcie i obróć palce wokół siebie, aby wywołać manipulację rotacją. Zwykle to obiekt obraca.
Jednocześnie może wystąpić więcej niż jeden typ manipulacji.
Gdy obiekty reagują na manipulacje, może pojawić się, że obiekt ma inercję. Może to sprawić, że obiekty symulują świat fizyczny. Na przykład, gdy przesuniesz książkę po stole, jeśli zrobisz to wystarczająco mocno, książka będzie nadal się poruszać po jej puszczeniu. WPF umożliwia symulowanie tego zachowania przez generowanie zdarzeń manipulacji po tym, jak palce użytkownika puszczają obiekt.
Aby uzyskać informacje o sposobie tworzenia aplikacji, która umożliwia użytkownikowi przenoszenie, zmienianie rozmiaru i obracanie obiektu, zobacz Przewodnik: tworzenie pierwszej aplikacji dotykowej.
UIElement definiuje następujące zdarzenia manipulowania.
Domyślnie UIElement nie odbiera tych zdarzeń manipulowania. Aby odbierać zdarzenia manipulowania w UIElement, ustaw UIElement.IsManipulationEnabled na true
.
Przebieg wydarzeń manipulacyjnych
Rozważmy scenariusz, w którym użytkownik "rzuca" obiekt. Użytkownik kładzie palec na obiekcie, przesuwa palcem po ekranie dotykowym na krótkim dystansie, a następnie podnosi palec podczas poruszania. Wynikiem tego jest to, że obiekt zostanie przeniesiony pod palec użytkownika i będzie nadal poruszać się po tym, jak użytkownik podnosi palec.
Na poniższej ilustracji przedstawiono ścieżkę wykonywania zdarzeń manipulowania i ważne informacje o każdym zdarzeniu.
Zdarzenia manipulowania
Poniższa lista zawiera opis sekwencji zdarzeń na poprzedniej ilustracji.
Zdarzenie ManipulationStarting występuje, gdy użytkownik umieszcza palec na obiekcie. Między innymi to zdarzenie umożliwia ustawienie właściwości ManipulationContainer. W kolejnych zdarzeniach pozycja manipulacji będzie względem ManipulationContainer. W przypadku zdarzeń innych niż ManipulationStartingta właściwość jest tylko do odczytu, więc zdarzenie ManipulationStarting jest jedynym czasem, w którym można ustawić tę właściwość.
Zdarzenie ManipulationStarted nastąpi w następnej kolejności. To zdarzenie zgłasza źródło manipulacji.
Zdarzenie ManipulationDelta występuje wielokrotnie, gdy palce użytkownika poruszają się na ekranie dotykowym. Właściwość DeltaManipulation klasy ManipulationDeltaEventArgs zgłasza, czy manipulacja jest interpretowana jako ruch, rozszerzanie lub tłumaczenie. W tym miejscu wykonujesz większość pracy podczas manipulowania obiektem.
Zdarzenie ManipulationInertiaStarting występuje, gdy palce użytkownika tracą kontakt z obiektem. To zdarzenie umożliwia określenie opóźnienia manipulacji podczas inercji. Jest to tak, aby obiekt mógł emulować różne przestrzenie fizyczne lub atrybuty, jeśli wybierzesz. Załóżmy na przykład, że aplikacja ma dwa obiekty reprezentujące elementy w świecie fizycznym, a jeden jest cięższy niż drugi. Można sprawić, że cięższy obiekt zwolnić szybciej niż lżejszy obiekt.
Zdarzenie ManipulationDelta występuje wiele razy, gdy zachodzi inercja. Należy pamiętać, że to zdarzenie występuje, gdy palce użytkownika poruszają się po ekranie dotykowym i gdy WPF symuluje inercję. Innymi słowy, ManipulationDelta występuje przed zdarzeniem ManipulationInertiaStarting i po nim. Właściwość ManipulationDeltaEventArgs.IsInertial zgłasza, czy zdarzenie ManipulationDelta występuje podczas inercji, aby można było sprawdzić tę właściwość i wykonać różne akcje w zależności od jej wartości.
Zdarzenie ManipulationCompleted występuje, gdy kończy się manipulacja i jakakolwiek inercja. Oznacza to, że po wystąpieniu wszystkich zdarzeń ManipulationDelta zdarzenie ManipulationCompleted ma na celu sygnalizowanie ukończenia manipulacji.
UIElement definiuje również zdarzenie ManipulationBoundaryFeedback. To zdarzenie występuje, gdy metoda ReportBoundaryFeedback jest wywoływana w zdarzeniu ManipulationDelta. Zdarzenie ManipulationBoundaryFeedback umożliwia aplikacjom lub składnikom przekazywanie opinii wizualnych, gdy obiekt osiągnie granicę. Na przykład klasa Window obsługuje zdarzenie ManipulationBoundaryFeedback, aby spowodować nieznaczne przesunięcie okna po napotkaniu krawędzi.
Manipulowanie można anulować, wywołując metodę Cancel na argumentach tego zdarzenia w dowolnym zdarzeniu manipulacji, z wyjątkiem zdarzenia ManipulationBoundaryFeedback. Po wywołaniu Cancelzdarzenia manipulacji przestają być generowane, a zdarzenia myszy występują w przypadku dotyku. W poniższej tabeli opisano relację między czasem anulowania manipulacji a zdarzeniami myszy, które występują.
Zdarzenie, które anuluj jest wywoływane w | Zdarzenia myszy występujące dla danych wejściowych, które już wystąpiły |
---|---|
ManipulationStarting i ManipulationStarted | Zdarzenia kliknięcia myszy. |
ManipulationDelta | Zdarzenia naciśnięcia i poruszania myszy. |
ManipulationInertiaStarting i ManipulationCompleted | Zdarzenia kliknięcia, ruchu i zwolnienia przycisku myszy. |
Należy pamiętać, że jeśli wywołasz metodę Cancel, gdy manipulacja jest w fazie bezwładności, metoda zwraca false
, a dane wejściowe nie wywołują zdarzeń myszy.
Relacja między zdarzeniami dotyku i manipulowania
UIElement zawsze może odbierać zdarzenia dotykowe. Gdy właściwość IsManipulationEnabled jest ustawiona na true
, UIElement może odbierać zdarzenia dotyku i manipulowania. Jeśli zdarzenie TouchDown nie jest obsługiwane (czyli właściwość Handled jest false
), logika manipulowania przechwytuje dotyk elementu i generuje zdarzenia manipulacji. Jeśli właściwość Handled jest ustawiona na true
w zdarzeniu TouchDown, logika manipulowania nie generuje zdarzeń manipulowania. Na poniższej ilustracji przedstawiono relację między zdarzeniami dotykowymi i zdarzeniami manipulowania.
Zdarzenia dotyku i manipulacji
Na poniższej liście opisano relację między zdarzeniami dotyku i manipulacji pokazanymi na poprzedniej ilustracji.
Gdy pierwsze urządzenie dotykowe generuje zdarzenie TouchDown na UIElement, logika manipulowania wywołuje metodę CaptureTouch, która generuje zdarzenie GotTouchCapture.
Gdy wystąpi GotTouchCapture, logika manipulowania wywołuje metodę Manipulation.AddManipulator, która generuje zdarzenie ManipulationStarting.
Gdy wystąpią zdarzenia TouchMove, logika manipulowania generuje zdarzenia ManipulationDelta, które występują przed zdarzeniem ManipulationInertiaStarting.
Gdy ostatnie urządzenie dotykowe na elemecie zgłasza zdarzenie TouchUp, logika manipulowania generuje zdarzenie ManipulationInertiaStarting.
Skupienie
Istnieją dwa główne pojęcia, które odnoszą się do koncentracji uwagi w WPF: fokus klawiatury i fokus logiczny.
Fokus klawiatury
Fokus klawiatury odnosi się do elementu odbierającego dane wejściowe klawiatury. Na całym pulpicie może znajdować się tylko jeden element, który ma fokus klawiatury. W WPF element z fokusem klawiatury będzie miał IsKeyboardFocused ustawiony na true
. Metoda statyczna KeyboardFocusedElement zwraca element, który obecnie ma fokus klawiatury.
Fokus klawiatury można uzyskać za pomocą klawisza Tab do elementu lub przez kliknięcie myszą na niektórych elementach, takich jak TextBox. Fokus klawiatury można również uzyskać programowo przy użyciu metody Focus w klasie Keyboard. Focus próbuje nadać określonemu elementowi fokus klawiatury. Element zwrócony przez Focus to ten, który ma obecnie fokus klawiatury.
Aby element mógł uzyskać fokus klawiatury, właściwość Focusable i właściwości IsVisible muszą być ustawione na true. Niektóre klasy, takie jak Panel, mają domyślnie ustawioną Focusable na false
; W związku z tym może być konieczne ustawienie tej właściwości na true
, jeśli chcesz, aby ten element mógł uzyskać fokus.
W poniższym przykładzie użyto Focus, aby ustawić fokus klawiatury na Button. Zalecanym miejscem ustawiania początkowego fokusu w aplikacji jest program obsługi zdarzeń Loaded.
private void OnLoaded(object sender, RoutedEventArgs e)
{
// Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Sets keyboard focus on the first Button in the sample.
Keyboard.Focus(firstButton)
End Sub
Aby uzyskać więcej informacji o fokusie klawiatury, zobacz Focus Overview.
Fokus logiczny
Fokus logiczny odnosi się do FocusManager.FocusedElement w obszarze zasięgu focusu. W aplikacji może istnieć wiele elementów, które mają fokus logiczny, ale może istnieć tylko jeden element, który ma fokus logiczny w określonym zakresie koncentracji uwagi.
Zakres fokusu to element kontenera, który śledzi FocusedElement w jego zakresie. Gdy fokus pozostawia zakres fokusu, element fokusu na klawiaturze utraci fokus, ale zachowa fokus logiczny. Gdy fokus powróci do zakresu koncentracji uwagi, element fokusu uzyska fokus klawiatury. Dzięki temu można zmieniać fokus klawiatury między wieloma obszarami fokusu, jednocześnie zapewniając, że element pozostaje wybrany w obszarze fokusu po powrocie fokusu.
Element można przekształcić w zakres fokusu w języku XAML (Extensible Application Markup Language), ustawiając właściwość dołączoną FocusManagerIsFocusScope na true
lub w kodzie, ustawiając dołączoną właściwość przy użyciu metody SetIsFocusScope.
Ustawienie dołączonej właściwości IsFocusScope sprawia, że StackPanel staje się zakresem uwagi w poniższym przykładzie.
<StackPanel Name="focusScope1"
FocusManager.IsFocusScope="True"
Height="200" Width="200">
<Button Name="button1" Height="50" Width="50"/>
<Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)
Klasy w WPF, które są zakresami fokusu domyślnie są Window, Menu, ToolBari ContextMenu.
Element, który ma fokus klawiatury, będzie również miał logiczny fokus dla zakresu fokusu, do którego należy; dlatego ustawienie fokusu na elemencie za pomocą metody Focus w klasie Keyboard lub klasach elementów podstawowych podejmie próbę nadania elementowi zarówno fokusu klawiatury, jak i fokusu logicznego.
Aby określić element fokusu w zakresie koncentracji uwagi, użyj GetFocusedElement. Aby zmienić element fokusu dla zakresu fokusu, użyj SetFocusedElement.
Aby uzyskać więcej informacji na temat fokusu logicznego, zobacz Focus Overview.
Położenie myszy
Interfejs API danych wejściowych WPF zawiera przydatne informacje dotyczące przestrzeni współrzędnych. Na przykład współrzędna (0,0)
to współrzędna lewego górnego rogu, ale którego elementu w drzewie? Element, który jest obiektem docelowym danych wejściowych? Element, do którego dołączyłeś swój obsługiwacz zdarzeń? Czy coś innego? Aby uniknąć nieporozumień, interfejs API danych wejściowych WPF wymaga określenia ramki odwołania podczas pracy ze współrzędnymi uzyskanymi za pośrednictwem myszy. Metoda GetPosition zwraca współrzędną wskaźnika myszy względem określonego elementu.
Przechwytywanie myszy
Urządzenia myszy posiadają w szczególności modalną cechę znaną jako przechwytywanie myszy. Przechwytywanie myszy służy do utrzymania przejściowego stanu wejściowego podczas uruchamiania operacji przeciągania i upuszczania, dzięki czemu inne operacje obejmujące nominalne położenie wskaźnika myszy niekoniecznie występują. Podczas przeciągania użytkownik nie może kliknąć bez przerywania przeciągania i upuszczania, co sprawia, że większość wskazówek myszy jest nieodpowiednia, podczas gdy przechwytywanie myszy jest trzymane przez źródło przeciągania. System wejściowy uwidacznia interfejsy API, które mogą określać stan przechwytywania myszy, a także interfejsy API, które mogą wymusić przechwytywanie myszy do określonego elementu lub wyczyść stan przechwytywania myszy. Aby uzyskać więcej informacji na temat operacji przeciągania i upuszczania, zobacz Przeciąganie i upuszczanie — omówienie.
Polecenia
Polecenia umożliwiają obsługę danych wejściowych na bardziej semantycznym poziomie niż dane wejściowe urządzenia. Polecenia to proste dyrektywy, takie jak Cut
, Copy
, Paste
lub Open
. Polecenia są przydatne do centralizacji logiki poleceń. Do tego samego polecenia można uzyskać dostęp w Menu, na ToolBarlub za pomocą skrótu klawiaturowego. Polecenia zapewniają również mechanizm wyłączania kontrolek, gdy polecenie stanie się niedostępne.
RoutedCommand jest implementacją WPF ICommand. Po wykonaniu RoutedCommand zdarzenie PreviewExecuted i Executed są wywoływane w obiekcie docelowym polecenia, które tunelują i bąbelkują przez drzewo elementów, takie jak inne dane wejściowe. Jeśli element docelowy polecenia nie jest ustawiony, element z fokusem klawiatury będzie obiektem docelowym polecenia. Logika wykonująca polecenie jest dołączana do CommandBinding. Gdy zdarzenie Executed osiągnie CommandBinding dla tego konkretnego polecenia, wywoływana jest ExecutedRoutedEventHandler na CommandBinding. Ten obsługiwacz wykonuje działanie polecenia.
Aby uzyskać więcej informacji na temat poleceń, zobacz Commanding Overview.
WPF udostępnia bibliotekę typowych poleceń, które składają się z ApplicationCommands, MediaCommands, ComponentCommands, NavigationCommandsi EditingCommandslub można zdefiniować własne.
W poniższym przykładzie pokazano, jak skonfigurować MenuItem tak, aby po kliknięciu wywołało Paste polecenie na TextBox, przy założeniu, że TextBox ma fokus klawiatury.
<StackPanel>
<Menu>
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;
// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;
' Creating the UI objects
Dim mainStackPanel As New StackPanel()
Dim pasteTextBox As New TextBox()
Dim stackPanelMenu As New Menu()
Dim pasteMenuItem As New MenuItem()
' Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem)
mainStackPanel.Children.Add(stackPanelMenu)
mainStackPanel.Children.Add(pasteTextBox)
' Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste
Aby uzyskać więcej informacji na temat poleceń w WPF, zobacz Commanding Overview.
System wejściowy i elementy podstawowe
Zdarzenia wejściowe, takie jak dołączone zdarzenia zdefiniowane przez klasy Mouse, Keyboardi Stylus, są wywoływane przez system wejściowy i wstrzykiwane do określonego miejsca w modelu obiektów na podstawie wyników testowania drzewa wizualnego w czasie wykonywania.
Każde zdarzenie, które Mouse, Keyboardi Stylus zdefiniować jako dołączone zdarzenie, jest również ponownie uwidocznione przez klasy elementów podstawowych UIElement i ContentElement jako nowe zdarzenie kierowane. Zdarzenia kierowane przez element podstawowy są generowane przez klasy obsługujące oryginalne dołączone zdarzenie i ponownie używając danych zdarzenia.
Gdy zdarzenie wejściowe zostanie skojarzone z konkretnym elementem źródłowym za pośrednictwem implementacji zdarzeń wejściowych elementu podstawowego, może być kierowane przez pozostałą część trasy zdarzeń opartej na kombinacji obiektów drzewa logicznego i wizualnego i obsługiwanego przez kod aplikacji. Ogólnie rzecz biorąc, bardziej wygodne jest obsługę tych zdarzeń wejściowych związanych z urządzeniem przy użyciu zdarzeń kierowanych na UIElement i ContentElement, ponieważ można użyć bardziej intuicyjnej składni obsługi zdarzeń zarówno w języku XAML, jak i w kodzie. Możesz zamiast tego obsłużyć dołączone zdarzenie, które zainicjowało proces, ale napotkasz kilka problemów: dołączone zdarzenie może być oznaczone jako już obsłużone przez przetwarzanie klasy bazowej elementu, a do dołączenia programów obsługi dla dołączonych zdarzeń musisz użyć metod dostępowych zamiast standardowej składni zdarzeń.
Co dalej
Masz teraz kilka technik do obsługi danych wejściowych w WPF. Należy również lepiej zrozumieć różne typy zdarzeń wejściowych i mechanizmów zdarzeń kierowanych używanych przez WPF.
Dostępne są dodatkowe zasoby, które bardziej szczegółowo wyjaśniają elementy struktury WPF i routing zdarzeń. Zobacz następujące omówienia, aby uzyskać więcej informacji: Omówienie poleceń, Omówienie skupienia, Omówienie podstawowych elementów, Drzewa w WPForaz Omówienie zdarzeń trasowanych.
Zobacz też
.NET Desktop feedback