Sdílet prostřednictvím


Zpracování vstupu ukazatele

V aplikacích pro Windows můžete přijímat, zpracovávat a spravovat vstupní data z ukazovacích zařízení (například dotykové ovládání, myš, pero/stylus a touchpad).

Důležité

Vytvořte vlastní interakce jenom v případě, že existuje jasný, dobře definovaný požadavek a interakce podporované ovládacími prvky platformy váš scénář nepodporují.
Pokud přizpůsobíte prostředí interakce ve vaší aplikaci pro Windows, uživatelé očekávají, že budou konzistentní, intuitivní a zjistitelné. Z těchto důvodů doporučujeme modelovat vlastní interakce s interakcemi podporovanými ovládacími prvky platformy. Ovládací prvky platformy poskytují kompletní prostředí interakce uživatelů aplikací pro Windows, včetně standardních interakcí, animovaných fyzikálních efektů, vizuální zpětné vazby a přístupnosti.

Důležitá rozhraní API

Ukazatele

Většina interakčních zkušeností obvykle zahrnuje uživatele, který identifikuje objekt, se kterým chce pracovat, tím, že na něj ukazuje prostřednictvím vstupních zařízení, jako je dotyková obrazovka, myš, pero/stylus a touchpad. Vzhledem k tomu, že nezpracovaná data zařízení pro lidské rozhraní (HID) poskytovaná těmito vstupními zařízeními zahrnují mnoho společných vlastností, data se propagují a konsolidují do sjednoceného vstupního zásobníku a zobrazují se jako data ukazatele nezávislá na zařízení. Vaše aplikace pro Windows pak můžou tato data využívat, aniž by se museli starat o používané vstupní zařízení.

Poznámka:

Informace specifické pro zařízení se také propagují z nezpracovaných dat HID, pokud je vaše aplikace vyžaduje.

Každý vstupní bod (nebo kontakt) ve vstupním zásobníku je reprezentován objektem Pointer vystaveným prostřednictvím parametru PointerRoutedEventArgs v různých obslužných rutinách událostí ukazatele. V případě víceperového nebo vícedotykového vstupu se každý kontakt považuje za jedinečný vstupní ukazatel.

Události ukazatele

Události ukazatele zpřístupňují základní informace, jako je typ vstupního zařízení a stav detekce (v rozsahu nebo kontaktu) a rozšířené informace, jako je umístění, tlak a geometrie kontaktu. Kromě toho jsou k dispozici také konkrétní vlastnosti zařízení, například jaké tlačítko myši uživatel stiskl nebo zda se používá guma na konci pera. Pokud vaše aplikace potřebuje rozlišovat mezi vstupními zařízeními a jejich funkcemi, přečtěte si téma Identifikace vstupních zařízení.

Aplikace pro Windows můžou naslouchat následujícím událostem ukazatele:

Poznámka:

Omezte vstup ukazatele na určitý prvek uživatelského rozhraní voláním CapturePointer na tento prvek v rámci obslužné rutiny události ukazatele. Pokud je ukazatel zachycen elementem, pouze tento objekt přijímá vstupní události ukazatele, i když se ukazatel přesune mimo ohraničující oblast objektu. IsInContact (tlačítko myši stisknuté, dotykové ovládání nebo pero v kontaktu) musí být pravdivé, aby funkce CapturePointer byla úspěšná.

Event Description

ZrušenoUkazatel

Nastane, když je ukazatel zrušen platformou. K tomu může dojít za následujících okolností:

  • Dotykové ukazatele se zruší, když se zjistí pero v rozsahu vstupní plochy.
  • Aktivní kontakt se nezjistí déle než 100 ms.
  • Monitor se změní (rozlišení, nastavení, konfigurace s více monitory).
  • Plocha je uzamčená nebo se uživatel odhlásil.
  • Počet souběžných kontaktů překročil počet podporovaný zařízením.

ZtrátaZachyceníUkazatele

Nastane, když jiný prvek uživatelského rozhraní zachycuje ukazatel, byl vydán ukazatel nebo byl programově zachycen jiný ukazatel.

Poznámka Neexistuje žádná odpovídající událost zachycení ukazatele.
 

PointerEntered

Nastane, když ukazatel přejde do ohraničující oblasti prvku. K tomu může dojít mírně různými způsoby pro dotykové ovládání, touchpad, myš a vstup pera.

  • Dotykové ovládání vyžaduje, aby kontakt prstem aktivoval tuto událost, a to buď z přímého dotyku prvku, nebo z pohybu do ohraničující oblasti prvku.
  • Myš i touchpad mají kurzor na obrazovce, který je vždy viditelný a aktivuje tuto událost, i když není stisknuto žádné tlačítko myši nebo touchpadu.
  • Podobně jako dotyk, pero aktivuje tuto událost přímým dotykem na prvek nebo přiblížením do ohraničující oblasti prvku. Pero má také stav přiblížení (IsInRange), který, když je pravdivý, aktivuje tuto událost.

PointerExited

Nastane, když ukazatel opustí ohraničující oblast prvku. K tomu může dojít mírně různými způsoby pro dotykové ovládání, touchpad, myš a vstup pera.

  • Dotykové ovládání vyžaduje kontakt prstem a aktivuje tuto událost, když se ukazatel přesune mimo ohraničující oblast prvku.
  • Myš i touchpad mají kurzor na obrazovce, který je vždy viditelný a aktivuje tuto událost, i když není stisknuto žádné tlačítko myši nebo touchpadu.
  • Podobně jako dotykové ovládání pero aktivuje tuto událost při pohybu mimo ohraničující oblast prvku. Pero má také stav při pohybu (IsInRange), který spouští tuto událost, když se stav změní z true na false.

ukazatel se pohnul

Nastane, když ukazatel změní souřadnice, stav tlačítka, tlak, naklonění nebo geometrii kontaktu (například šířku a výšku) v ohraničující oblasti prvku. K tomu může dojít mírně různými způsoby pro dotykové ovládání, touchpad, myš a vstup pera.

  • Dotykové ovládání vyžaduje kontakt prstem a aktivuje tuto událost pouze v případě, že je kontakt v ohraničující oblasti prvku.
  • Myš i touchpad mají kurzor na obrazovce, který je vždy viditelný a aktivuje tuto událost, i když není stisknuto žádné tlačítko myši nebo touchpadu.
  • Podobně jako dotykové ovládání pero tuto událost aktivuje při kontaktu v ohraničující oblasti prvku. Pero má ale také stav najetí (IsInRange), který, pokud je pravda a nachází se v rámci ohraničující oblasti prvku, vyvolá tuto událost.

PointerPressed

Nastane, když ukazatel označuje akci stisknutí (například dotyk dolů, stisknutí tlačítka myši, stisknutí pera nebo stisknutí tlačítka touchpadu) v ohraničující oblasti prvku.

CapturePointer musí být volána handlerem této události.

Uvolnění ukazatele

Nastane, když ukazatel indikuje akci uvolnění (například uvolnění dotyku, tlačítka myši, pera nebo tlačítka touchpadu) v rámci ohraničující oblasti prvku nebo, pokud je ukazatel zachycen, mimo ohraničující oblast.

PointerWheelChanged

Nastane, když je kolečko myši otočené.

Vstup myši je přidružený k jednomu ukazateli přiřazeným při prvním zjištění vstupu myši. Kliknutí na tlačítko myši (vlevo, kolečko nebo vpravo) vytvoří sekundární přidružení mezi ukazatelem a tímto tlačítkem prostřednictvím události PointerMoved .

 

Příklad události ukazatele

Tady jsou některé fragmenty kódu ze základní aplikace pro sledování ukazatelů, které ukazují, jak naslouchat a zpracovávat události pro více ukazatelů a získat různé vlastnosti přidružených ukazatelů.

Uživatelské rozhraní aplikace ukazatelů

Stáhnout tuto ukázku z Ukázka vstupu ukazatele (základní)

Vytvoření uživatelského rozhraní

V tomto příkladu jako vstup ukazatele používáme obdélník (Target). Barva cíle se změní, když se změní stav ukazatele.

Podrobnosti o každém ukazateli se zobrazí v plovoucím bloku TextBlock , který následuje za ukazatelem při přesouvání. Samotné události ukazatele jsou hlášeny v RichTextBlock napravo od obdélníku.

Toto je jazyk XAML (Extensible Application Markup Language) pro uživatelské rozhraní v tomto příkladu.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="250"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*" />
        <RowDefinition Height="320" />
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Canvas Name="Container" 
            Grid.Column="0"
            Grid.Row="1"
            HorizontalAlignment="Center" 
            VerticalAlignment="Center" 
            Margin="245,0" 
            Height="320"  Width="640">
        <Rectangle Name="Target" 
                    Fill="#FF0000" 
                    Stroke="Black" 
                    StrokeThickness="0"
                    Height="320" Width="640" />
    </Canvas>
    <Grid Grid.Column="1" Grid.Row="0" Grid.RowSpan="3">
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Button Name="buttonClear" 
                Grid.Row="0"
                Content="Clear"
                Foreground="White"
                HorizontalAlignment="Stretch" 
                VerticalAlignment="Stretch">
        </Button>
        <ScrollViewer Name="eventLogScrollViewer" Grid.Row="1" 
                        VerticalScrollMode="Auto" 
                        Background="Black">                
            <RichTextBlock Name="eventLog"  
                        TextWrapping="Wrap" 
                        Foreground="#FFFFFF" 
                        ScrollViewer.VerticalScrollBarVisibility="Visible" 
                        ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                        Grid.ColumnSpan="2">
            </RichTextBlock>
        </ScrollViewer>
    </Grid>
</Grid>

Naslouchání událostem ukazatele

Ve většině případů doporučujeme získat informace o ukazateli prostřednictvím PointerRoutedEventArgs obslužné rutiny události.

Pokud argument události nezpřístupňuje požadované podrobnosti ukazatele, můžete získat přístup k rozšířeným informacím o ukazateli vystaveným prostřednictvím metod GetCurrentPoint a GetIntermediatePointsPointerRoutedEventArgs.

Následující kód nastaví objekt globálního slovníku pro sledování každého aktivního ukazatele a identifikuje různé naslouchací procesy událostí ukazatele pro cílový objekt.

// Dictionary to maintain information about each active pointer. 
// An entry is added during PointerPressed/PointerEntered events and removed 
// during PointerReleased/PointerCaptureLost/PointerCanceled/PointerExited events.
Dictionary<uint, Windows.UI.Xaml.Input.Pointer> pointers;

public MainPage()
{
    this.InitializeComponent();

    // Initialize the dictionary.
    pointers = new Dictionary<uint, Windows.UI.Xaml.Input.Pointer>();

    // Declare the pointer event handlers.
    Target.PointerPressed += 
        new PointerEventHandler(Target_PointerPressed);
    Target.PointerEntered += 
        new PointerEventHandler(Target_PointerEntered);
    Target.PointerReleased += 
        new PointerEventHandler(Target_PointerReleased);
    Target.PointerExited += 
        new PointerEventHandler(Target_PointerExited);
    Target.PointerCanceled += 
        new PointerEventHandler(Target_PointerCanceled);
    Target.PointerCaptureLost += 
        new PointerEventHandler(Target_PointerCaptureLost);
    Target.PointerMoved += 
        new PointerEventHandler(Target_PointerMoved);
    Target.PointerWheelChanged += 
        new PointerEventHandler(Target_PointerWheelChanged);

    buttonClear.Click += 
        new RoutedEventHandler(ButtonClear_Click);
}

Zpracování událostí ukazatele

V dalším kroku použijeme zpětnou vazbu uživatelského rozhraní k předvedení základních obslužných rutin událostí ukazatele.

/// <summary>
/// The pointer pressed event handler.
/// PointerPressed and PointerReleased don't always occur in pairs. 
/// Your app should listen for and handle any event that can conclude 
/// a pointer down (PointerExited, PointerCanceled, PointerCaptureLost).
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
void Target_PointerPressed(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Down: " + ptrPt.PointerId);

    // Lock the pointer to the target.
    Target.CapturePointer(e.Pointer);

    // Update event log.
    UpdateEventLog("Pointer captured: " + ptrPt.PointerId);

    // Check if pointer exists in dictionary (ie, enter occurred prior to press).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    // Change background color of target when pointer contact detected.
    Target.Fill = new SolidColorBrush(Windows.UI.Colors.Green);

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerEntered . Událost přidáme do protokolu událostí, přidáme ukazatel na kolekci ukazatelů a zobrazíme podrobnosti ukazatele.
/// <summary>
/// The pointer entered event handler.
/// We do not capture the pointer on this event.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Entered: " + ptrPt.PointerId);

    // Check if pointer already exists (if enter occurred prior to down).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    if (pointers.Count == 0)
    {
        // Change background color of target when pointer contact detected.
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Blue);
    }

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerMoved . Událost přidáme do protokolu událostí a aktualizujeme podrobnosti ukazatele.

    Důležité

    Vstup myši je přidružený k jednomu ukazateli přiřazeným při prvním zjištění vstupu myši. Kliknutí na tlačítko myši (vlevo, kolečko nebo vpravo) vytvoří sekundární přidružení mezi ukazatelem a tímto tlačítkem prostřednictvím události PointerPressed . Událost PointerReleased se aktivuje pouze v případě, že je vydáno stejné tlačítko myši (dokud se tato událost nedokončuje, není možné k této události přidružit žádné jiné tlačítko). Vzhledem k tomuto výhradnímu přidružení se ostatní kliknutí na tlačítko myši směrují přes událost PointerMoved .  

/// <summary>
/// The pointer moved event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerMoved(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Multiple, simultaneous mouse button inputs are processed here.
    // Mouse input is associated with a single pointer assigned when 
    // mouse input is first detected. 
    // Clicking additional mouse buttons (left, wheel, or right) during 
    // the interaction creates secondary associations between those buttons 
    // and the pointer through the pointer pressed event. 
    // The pointer released event is fired only when the last mouse button 
    // associated with the interaction (not necessarily the initial button) 
    // is released. 
    // Because of this exclusive association, other mouse button clicks are 
    // routed through the pointer move event.          
    if (ptrPt.PointerDevice.PointerDeviceType == Windows.Devices.Input.PointerDeviceType.Mouse)
    {
        if (ptrPt.Properties.IsLeftButtonPressed)
        {
            UpdateEventLog("Left button: " + ptrPt.PointerId);
        }
        if (ptrPt.Properties.IsMiddleButtonPressed)
        {
            UpdateEventLog("Wheel button: " + ptrPt.PointerId);
        }
        if (ptrPt.Properties.IsRightButtonPressed)
        {
            UpdateEventLog("Right button: " + ptrPt.PointerId);
        }
    }

    // Display pointer details.
    UpdateInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerWheelChanged . Událost přidáme do protokolu událostí, přidáme ukazatel na pole ukazatele (v případě potřeby) a zobrazíme podrobnosti ukazatele.
/// <summary>
/// The pointer wheel event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Mouse wheel: " + ptrPt.PointerId);

    // Check if pointer already exists (for example, enter occurred prior to wheel).
    if (!pointers.ContainsKey(ptrPt.PointerId))
    {
        // Add contact to dictionary.
        pointers[ptrPt.PointerId] = e.Pointer;
    }

    // Display pointer details.
    CreateInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerReleased , kde je ukončen kontakt s digitizátorem. Událost přidáme do protokolu událostí, odebereme ukazatel z kolekce ukazatelů a aktualizujeme podrobnosti ukazatele.
/// <summary>
/// The pointer released event handler.
/// PointerPressed and PointerReleased don't always occur in pairs. 
/// Your app should listen for and handle any event that can conclude 
/// a pointer down (PointerExited, PointerCanceled, PointerCaptureLost).
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
void Target_PointerReleased(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Up: " + ptrPt.PointerId);

    // If event source is mouse or touchpad and the pointer is still 
    // over the target, retain pointer and pointer details.
    // Return without removing pointer from pointers dictionary.
    // For this example, we assume a maximum of one mouse pointer.
    if (ptrPt.PointerDevice.PointerDeviceType != Windows.Devices.Input.PointerDeviceType.Mouse)
    {
        // Update target UI.
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Red);

        DestroyInfoPop(ptrPt);

        // Remove contact from dictionary.
        if (pointers.ContainsKey(ptrPt.PointerId))
        {
            pointers[ptrPt.PointerId] = null;
            pointers.Remove(ptrPt.PointerId);
        }

        // Release the pointer from the target.
        Target.ReleasePointerCapture(e.Pointer);

        // Update event log.
        UpdateEventLog("Pointer released: " + ptrPt.PointerId);
    }
    else
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Blue);
    }
}
  • Tato obslužná rutina spravuje událost PointerExited (pokud je zachován kontakt s digitizátorem). Událost přidáme do protokolu událostí, odebereme ukazatel z pole ukazatele a aktualizujeme podrobnosti ukazatele.
/// <summary>
/// The pointer exited event handler.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerExited(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer exited: " + ptrPt.PointerId);

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Red);
    }

    // Update the UI and pointer details.
    DestroyInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerCanceled . Událost přidáme do protokolu událostí, odebereme ukazatel z pole ukazatele a aktualizujeme podrobnosti ukazatele.
/// <summary>
/// The pointer canceled event handler.
/// Fires for various reasons, including: 
///    - Touch contact canceled by pen coming into range of the surface.
///    - The device doesn't report an active contact for more than 100ms.
///    - The desktop is locked or the user logged off. 
///    - The number of simultaneous contacts exceeded the number supported by the device.
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerCanceled(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer canceled: " + ptrPt.PointerId);

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Black);
    }

    DestroyInfoPop(ptrPt);
}
  • Tato obslužná rutina spravuje událost PointerCaptureLost . Událost přidáme do protokolu událostí, odebereme ukazatel z pole ukazatele a aktualizujeme podrobnosti ukazatele.

    Poznámka:

    PointerCaptureLost může nastat místo PointerReleased. Zachycení ukazatele může být ztraceno z různých důvodů, včetně interakce uživatele, programového zachycení jiného ukazatele, volání PointerReleased.  

/// <summary>
/// The pointer capture lost event handler.
/// Fires for various reasons, including: 
///    - User interactions
///    - Programmatic capture of another pointer
///    - Captured pointer was deliberately released
// PointerCaptureLost can fire instead of PointerReleased. 
/// </summary>
/// <param name="sender">Source of the pointer event.</param>
/// <param name="e">Event args for the pointer routed event.</param>
private void Target_PointerCaptureLost(object sender, PointerRoutedEventArgs e)
{
    // Prevent most handlers along the event route from handling the same event again.
    e.Handled = true;

    PointerPoint ptrPt = e.GetCurrentPoint(Target);

    // Update event log.
    UpdateEventLog("Pointer capture lost: " + ptrPt.PointerId);

    if (pointers.Count == 0)
    {
        Target.Fill = new SolidColorBrush(Windows.UI.Colors.Black);
    }

    // Remove contact from dictionary.
    if (pointers.ContainsKey(ptrPt.PointerId))
    {
        pointers[ptrPt.PointerId] = null;
        pointers.Remove(ptrPt.PointerId);
    }

    DestroyInfoPop(ptrPt);
}

Získání vlastností ukazatele

Jak jsme uvedli dříve, musíte získat většinu rozšířených informací o ukazatelích z objektu Windows.UI.Input.PointerPoint získaného prostřednictvím Metod GetCurrentPoint a GetIntermediatePointsPointerRoutedEventArgs. Následující fragmenty kódu ukazují, jak na to.

  • Nejprve vytvoříme nový TextBlock pro každý ukazatel.
/// <summary>
/// Create the pointer info popup.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
void CreateInfoPop(PointerPoint ptrPt)
{
    TextBlock pointerDetails = new TextBlock();
    pointerDetails.Name = ptrPt.PointerId.ToString();
    pointerDetails.Foreground = new SolidColorBrush(Windows.UI.Colors.White);
    pointerDetails.Text = QueryPointer(ptrPt);

    TranslateTransform x = new TranslateTransform();
    x.X = ptrPt.Position.X + 20;
    x.Y = ptrPt.Position.Y + 20;
    pointerDetails.RenderTransform = x;

    Container.Children.Add(pointerDetails);
}
  • Pak poskytujeme způsob, jak aktualizovat informace o ukazateli v existujícím objektu TextBlock přidruženém k tomu ukazateli.
/// <summary>
/// Update the pointer info popup.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
void UpdateInfoPop(PointerPoint ptrPt)
{
    foreach (var pointerDetails in Container.Children)
    {
        if (pointerDetails.GetType().ToString() == "Windows.UI.Xaml.Controls.TextBlock")
        {
            TextBlock textBlock = (TextBlock)pointerDetails;
            if (textBlock.Name == ptrPt.PointerId.ToString())
            {
                // To get pointer location details, we need extended pointer info.
                // We get the pointer info through the getCurrentPoint method
                // of the event argument. 
                TranslateTransform x = new TranslateTransform();
                x.X = ptrPt.Position.X + 20;
                x.Y = ptrPt.Position.Y + 20;
                pointerDetails.RenderTransform = x;
                textBlock.Text = QueryPointer(ptrPt);
            }
        }
    }
}
  • Nakonec se dotazujeme na různé vlastnosti ukazatele.
/// <summary>
/// Get pointer details.
/// </summary>
/// <param name="ptrPt">Reference to the input pointer.</param>
/// <returns>A string composed of pointer details.</returns>
String QueryPointer(PointerPoint ptrPt)
{
    String details = "";

    switch (ptrPt.PointerDevice.PointerDeviceType)
    {
        case Windows.Devices.Input.PointerDeviceType.Mouse:
            details += "\nPointer type: mouse";
            break;
        case Windows.Devices.Input.PointerDeviceType.Pen:
            details += "\nPointer type: pen";
            if (ptrPt.IsInContact)
            {
                details += "\nPressure: " + ptrPt.Properties.Pressure;
                details += "\nrotation: " + ptrPt.Properties.Orientation;
                details += "\nTilt X: " + ptrPt.Properties.XTilt;
                details += "\nTilt Y: " + ptrPt.Properties.YTilt;
                details += "\nBarrel button pressed: " + ptrPt.Properties.IsBarrelButtonPressed;
            }
            break;
        case Windows.Devices.Input.PointerDeviceType.Touch:
            details += "\nPointer type: touch";
            details += "\nrotation: " + ptrPt.Properties.Orientation;
            details += "\nTilt X: " + ptrPt.Properties.XTilt;
            details += "\nTilt Y: " + ptrPt.Properties.YTilt;
            break;
        default:
            details += "\nPointer type: n/a";
            break;
    }

    GeneralTransform gt = Target.TransformToVisual(this);
    Point screenPoint;

    screenPoint = gt.TransformPoint(new Point(ptrPt.Position.X, ptrPt.Position.Y));
    details += "\nPointer Id: " + ptrPt.PointerId.ToString() +
        "\nPointer location (target): " + Math.Round(ptrPt.Position.X) + ", " + Math.Round(ptrPt.Position.Y) +
        "\nPointer location (container): " + Math.Round(screenPoint.X) + ", " + Math.Round(screenPoint.Y);

    return details;
}

Primární ukazatel

Některá vstupní zařízení, jako je dotyková číslice nebo touchpad, podporují více než typický jeden ukazatel myši nebo pera (ve většině případů surface Hub podporuje dva vstupy pera).

K identifikaci a rozlišení jednoho primárního ukazatele použijte vlastnost IsPrimary jen pro čtení třídy PointerPointerProperties (primární ukazatel je vždy první ukazatel zjištěn během vstupní sekvence).

Když identifikujete primární ukazatel, můžete ho použít k emulaci vstupu myši nebo pera, přizpůsobení interakcí nebo poskytnutí některých dalších specifických funkcí nebo uživatelského rozhraní.

Poznámka:

Pokud se primární ukazatel uvolní, zruší nebo ztratí během vstupní sekvence, primární vstupní ukazatel se nevytvořil, dokud se nespustí nová vstupní sekvence (vstupní sekvence skončí, když byly vydány, zrušeny nebo ztraceny všechny ukazatele).

Příklad animace primárního ukazatele

Tyto fragmenty kódu ukazují, jak můžete poskytnout speciální vizuální zpětnou vazbu, která uživateli pomůže odlišit vstupy ukazatelů ve vaší aplikaci.

Tato konkrétní aplikace používá ke zvýraznění primárního ukazatele barvu i animaci.

Aplikace ukazatele s animovanou vizuální zpětnou vazbou

Stáhnout tuto ukázku z Ukázka vstupu ukazatele (UserControl s animací)

Vizuální zpětná vazba

Definujeme UserControl na základě objektu XAML Ellipse, který zvýrazní, kde je každý ukazatel na plátně, a pomocí Storyboardu animuje elipsu odpovídající primárnímu ukazateli.

Tady je XAML:

<UserControl
    x:Class="UWP_Pointers.PointerEllipse"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UWP_Pointers"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="100"
    d:DesignWidth="100">

    <UserControl.Resources>
        <Style x:Key="EllipseStyle" TargetType="Ellipse">
            <Setter Property="Transitions">
                <Setter.Value>
                    <TransitionCollection>
                        <ContentThemeTransition/>
                    </TransitionCollection>
                </Setter.Value>
            </Setter>
        </Style>
        
        <Storyboard x:Name="myStoryboard">
            <!-- Animates the value of a Double property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <DoubleAnimation
              Storyboard.TargetName="ellipse"
              Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"  
              Duration="0:0:1" 
              AutoReverse="True" 
              RepeatBehavior="Forever" From="1.0" To="1.4">
            </DoubleAnimation>

            <!-- Animates the value of a Double property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <DoubleAnimation
              Storyboard.TargetName="ellipse"
              Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"  
              Duration="0:0:1" 
              AutoReverse="True" 
              RepeatBehavior="Forever" From="1.0" To="1.4">
            </DoubleAnimation>

            <!-- Animates the value of a Color property between 
            two target values using linear interpolation over the 
            specified Duration. -->
            <ColorAnimation 
                Storyboard.TargetName="ellipse" 
                EnableDependentAnimation="True" 
                Storyboard.TargetProperty="(Fill).(SolidColorBrush.Color)" 
                From="White" To="Red"  Duration="0:0:1" 
                AutoReverse="True" RepeatBehavior="Forever"/>
        </Storyboard>
    </UserControl.Resources>

    <Grid x:Name="CompositionContainer">
        <Ellipse Name="ellipse" 
        StrokeThickness="2" 
        Width="{x:Bind Diameter}" 
        Height="{x:Bind Diameter}"  
        Style="{StaticResource EllipseStyle}" />
    </Grid>
</UserControl>

A tady je kód:

using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

// The User Control item template is documented at 
// https://go.microsoft.com/fwlink/?LinkId=234236

namespace UWP_Pointers
{
    /// <summary>
    /// Pointer feedback object.
    /// </summary>
    public sealed partial class PointerEllipse : UserControl
    {
        // Reference to the application canvas.
        Canvas canvas;

        /// <summary>
        /// Ellipse UI for pointer feedback.
        /// </summary>
        /// <param name="c">The drawing canvas.</param>
        public PointerEllipse(Canvas c)
        {
            this.InitializeComponent();
            canvas = c;
        }

        /// <summary>
        /// Gets or sets the pointer Id to associate with the PointerEllipse object.
        /// </summary>
        public uint PointerId
        {
            get { return (uint)GetValue(PointerIdProperty); }
            set { SetValue(PointerIdProperty, value); }
        }
        // Using a DependencyProperty as the backing store for PointerId.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PointerIdProperty =
            DependencyProperty.Register("PointerId", typeof(uint), 
                typeof(PointerEllipse), new PropertyMetadata(null));


        /// <summary>
        /// Gets or sets whether the associated pointer is Primary.
        /// </summary>
        public bool PrimaryPointer
        {
            get { return (bool)GetValue(PrimaryPointerProperty); }
            set
            {
                SetValue(PrimaryPointerProperty, value);
            }
        }
        // Using a DependencyProperty as the backing store for PrimaryPointer.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PrimaryPointerProperty =
            DependencyProperty.Register("PrimaryPointer", typeof(bool), 
                typeof(PointerEllipse), new PropertyMetadata(false));


        /// <summary>
        /// Gets or sets the ellipse style based on whether the pointer is Primary.
        /// </summary>
        public bool PrimaryEllipse 
        {
            get { return (bool)GetValue(PrimaryEllipseProperty); }
            set
            {
                SetValue(PrimaryEllipseProperty, value);
                if (value)
                {
                    SolidColorBrush fillBrush = 
                        (SolidColorBrush)Application.Current.Resources["PrimaryFillBrush"];
                    SolidColorBrush strokeBrush = 
                        (SolidColorBrush)Application.Current.Resources["PrimaryStrokeBrush"];

                    ellipse.Fill = fillBrush;
                    ellipse.Stroke = strokeBrush;
                    ellipse.RenderTransform = new CompositeTransform();
                    ellipse.RenderTransformOrigin = new Point(.5, .5);
                    myStoryboard.Begin();
                }
                else
                {
                    SolidColorBrush fillBrush = 
                        (SolidColorBrush)Application.Current.Resources["SecondaryFillBrush"];
                    SolidColorBrush strokeBrush = 
                        (SolidColorBrush)Application.Current.Resources["SecondaryStrokeBrush"];
                    ellipse.Fill = fillBrush;
                    ellipse.Stroke = strokeBrush;
                }
            }
        }
        // Using a DependencyProperty as the backing store for PrimaryEllipse.  
        // This enables animation, styling, binding, etc...
        public static readonly DependencyProperty PrimaryEllipseProperty =
            DependencyProperty.Register("PrimaryEllipse", 
                typeof(bool), typeof(PointerEllipse), new PropertyMetadata(false));


        /// <summary>
        /// Gets or sets the diameter of the PointerEllipse object.
        /// </summary>
        public int Diameter
        {
            get { return (int)GetValue(DiameterProperty); }
            set { SetValue(DiameterProperty, value); }
        }
        // Using a DependencyProperty as the backing store for Diameter.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DiameterProperty =
            DependencyProperty.Register("Diameter", typeof(int), 
                typeof(PointerEllipse), new PropertyMetadata(120));
    }
}

Vytvoření uživatelského rozhraní

Uživatelské rozhraní v tomto příkladu je omezeno na vstupní plátno , kde sledujeme všechny ukazatele a vykreslujeme indikátory ukazatele a animaci primárního ukazatele (pokud je k dispozici) spolu s pruhem záhlaví obsahujícím čítač ukazatele a identifikátor primárního ukazatele.

Tady je MainPage.xaml:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" 
                Orientation="Horizontal" 
                Grid.Row="0">
        <StackPanel.Transitions>
            <TransitionCollection>
                <AddDeleteThemeTransition/>
            </TransitionCollection>
        </StackPanel.Transitions>
        <TextBlock x:Name="Header" 
                    Text="Basic pointer tracking sample - IsPrimary" 
                    Style="{ThemeResource HeaderTextBlockStyle}" 
                    Margin="10,0,0,0" />
        <TextBlock x:Name="PointerCounterLabel"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="Number of pointers: " 
                    Margin="50,0,0,0"/>
        <TextBlock x:Name="PointerCounter"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="0" 
                    Margin="10,0,0,0"/>
        <TextBlock x:Name="PointerPrimaryLabel"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="Primary: " 
                    Margin="50,0,0,0"/>
        <TextBlock x:Name="PointerPrimary"
                    VerticalAlignment="Center"                 
                    Style="{ThemeResource BodyTextBlockStyle}"
                    Text="n/a" 
                    Margin="10,0,0,0"/>
    </StackPanel>
    
    <Grid Grid.Row="1">
        <!--The canvas where we render the pointer UI.-->
        <Canvas x:Name="pointerCanvas"/>
    </Grid>
</Grid>

Zpracování událostí ukazatele

Nakonec definujeme základní obslužné rutiny událostí ukazatele v kódu MainPage.xaml.cs, který je součástí code-behind. Kód zde nebudeme reprodukovat, protože základy byly popsány v předchozím příkladu, ale můžete si stáhnout pracovní ukázku ze vstupní ukázky ukazatele (UserControl s animací).

Ukázky témat

Další ukázky

Archivní ukázky