Dela via


Penninteraktioner och Windows Ink i Windows-appar

Hero-bild av Surface Pen.
Surface Pen (finns att köpa i Microsoft Store).

Översikt

Optimera Din Windows-app för penninmatning för att tillhandahålla både standardfunktioner för pekareenheter och den bästa Windows Ink-upplevelsen för dina användare.

Anmärkning

Det här avsnittet fokuserar på Windows Ink-plattformen. Allmän hantering av pekarindata (liknar mus, touch och pekplatta) finns i Hantera pekarindata.

Använda pennanteckning i din Windows-app

Använda Windows Pen and Ink för att skapa mer engagerande företagsappar

Windows Ink-plattformen, tillsammans med en pennenhet, är ett naturligt sätt att skapa digitala handskrivna anteckningar, ritningar och anteckningar. Plattformen stöder insamling av digitaliserardata som bläckdata, genererar bläckdata, hanterar bläckdata, återger bläckdata som bläckstreck på utdataenheten och konverterar bläck till text via handskriftsigenkänning.

Förutom att fånga pennans grundläggande position och rörelse när användaren skriver eller ritar, kan din app också spåra och samla in de varierande mängder tryck som används under en stroke. Med den här informationen, tillsammans med inställningar för pennspetsform, storlek och rotation, pennanteckningsfärg och syfte (oformaterad penna, radering, markering och val), kan du tillhandahålla användarupplevelser som liknar att skriva eller rita på papper med penna, penna eller pensel.

Anmärkning

Din app kan också stöda bläckinmatning från andra pekarbaserade enheter, inklusive beröringsdigitaliserare och möss. 

Bläckplattformen är mycket flexibel. Den är utformad för att stödja olika funktionalitetsnivåer, beroende på dina krav.

Riktlinjer för Windows Ink UX finns i Pennanteckningskontroller.

Komponenter på Windows Ink-plattformen

Komponent Description
InkCanvas En XAML UI-plattformskontroll som som standard tar emot och visar alla indata från en penna som antingen ett pennstreck eller ett raderingsdrag.
Mer information om hur du använder InkCanvas finns i Identifiera Windows Ink-pennstreck som text och Lagra och hämta data för Windows Ink-pennstreck.
InkPresenter Ett code-behind-objekt, instansierat tillsammans med en InkCanvas-kontroll (exponeras via egenskapen InkCanvas.InkPresenter ). Det här objektet innehåller alla standardfunktioner för pennanteckning som exponeras av InkCanvas, tillsammans med en omfattande uppsättning API:er för ytterligare anpassning och personaliseringsalternativ.
Mer information om hur du använder InkPresenter finns i Identifiera Windows Ink-linjer som text och Lagra och hämta Windows Ink-streckdata.
InkToolbar En XAML UI-plattformskontroll som innehåller en anpassningsbar och utökningsbar samling knappar som aktiverar pennanteckningsrelaterade funktioner i en associerad InkCanvas.
Mer information om hur du använder InkToolbar finns i Lägga till en InkToolbar i en Windows-app för pennanteckningsapp.
IInkD2DRenderer Möjliggör återgivning av pennstreck på den angivna Direct2D-enhetskontexten för en universell Windows-app, istället för att använda standardkontrollen InkCanvas. Detta möjliggör fullständig anpassning av pennanteckningsupplevelsen.
Mer information finns i exemplet med komplex pennanteckning.

Grundläggande pennanteckning med InkCanvas

Om du vill lägga till grundläggande pennanteckningsfunktioner placerar du bara en InkCanvas UWP-plattformskontroll på rätt sida i din app.

Som standard stöder InkCanvas endast pennanteckningar från en penna. Indatan visas antingen som ett bläckstreck med standardinställningar för färg och tjocklek (en svart kulpenna med en tjocklek på 2 bildpunkter), eller behandlas som ett radergummi (när indata kommer från en suddgummispets eller när pennspetsens funktion ändras med en raderingsknapp).

Anmärkning

Om det inte finns ett radergummitips eller en knapp kan InkCanvas konfigureras så att indata från pennspetsen behandlas som ett raderingsdrag.

I det här exemplet lägger en InkCanvas över en bakgrundsbild.

Anmärkning

En InkCanvas har standardegenskaperna Höjd och Bredd på noll, såvida det inte är underordnat till ett element som automatiskt storleksanpassar sina underordnade element, till exempel StackPanel - eller Grid-kontroller .

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />            
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

Den här serien med bilder visar hur pennindata återges av den här InkCanvas-kontrollen .

Skärmbild av den tomma InkCanvas med en bakgrundsbild. Skärmbild av InkCanvas med pennstreck. Skärmbild av InkCanvas med ett penseldrag raderat.
Den tomma InkCanvas med en bakgrundsbild. InkCanvas med bläckstreck. InkCanvas med ett streck raderat (observera hur radering fungerar på ett helt streck, inte en del).

Pennanteckningsfunktionen som stöds av InkCanvas-kontrollen tillhandahålls av ett code-behind-objekt som kallas InkPresenter.

För grundläggande pennanteckning behöver du inte bry dig om InkPresenter. Men om du vill anpassa och konfigurera pennanteckningsbeteende på InkCanvas måste du komma åt motsvarande InkPresenter-objekt .

Grundläggande anpassning med InkPresenter

Ett InkPresenter-objekt instansieras med varje InkCanvas-kontroll .

Anmärkning

InkPresenter kan inte instansieras direkt. I stället nås den via egenskapen InkPresenter för InkCanvas

Tillsammans med att tillhandahålla alla standardinmatningsbeteenden för motsvarande InkCanvas-kontroll tillhandahåller InkPresenter en omfattande uppsättning API:er för ytterligare linjeanpassning och finare kornig hantering av pennindata (standard och ändrad). Detta omfattar strokeegenskaper, enhetstyper som stöds och huruvida indata bearbetas av objektet eller skickas till appen för bearbetning.

Anmärkning

Standardbläckinmatning (från antingen pennspets eller suddgummispets/knapp) påverkas inte av sekundära hårdvarufunktioner, såsom en knapp på pennkroppen, höger musknapp eller liknande mekanism.

Som standard stöds pennanteckning endast för penninmatning. Här konfigurerar vi InkPresenter för att tolka indata från både penna och mus som pennstreck. Vi anger även några ursprungliga attribut för bläckstreck som används för att återge streck på InkCanvas.

Om du vill aktivera mus- och pekinmatning anger du egenskapen InputDeviceTypes för InkPresenter till den kombination av CoreInputDeviceTypes-värden som du vill använda.

public MainPage()
{
    this.InitializeComponent();

    // Set supported inking device types.
    inkCanvas.InkPresenter.InputDeviceTypes =
        Windows.UI.Core.CoreInputDeviceTypes.Mouse |
        Windows.UI.Core.CoreInputDeviceTypes.Pen;

    // Set initial ink stroke attributes.
    InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
    drawingAttributes.Color = Windows.UI.Colors.Black;
    drawingAttributes.IgnorePressure = false;
    drawingAttributes.FitToCurve = true;
    inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}

Attribut för pennstreck kan ställas in dynamiskt för att uppfylla användarinställningar eller appkrav.

Här låter vi en användare välja från en lista med bläckfärger.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink customization sample"
                   VerticalAlignment="Center"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
        <TextBlock Text="Color:"
                   Style="{StaticResource SubheaderTextBlockStyle}"
                   VerticalAlignment="Center"
                   Margin="50,0,10,0"/>
        <ComboBox x:Name="PenColor"
                  VerticalAlignment="Center"
                  SelectedIndex="0"
                  SelectionChanged="OnPenColorChanged">
            <ComboBoxItem Content="Black"/>
            <ComboBoxItem Content="Red"/>
        </ComboBox>
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

Sedan hanterar vi ändringar i den valda färgen och uppdaterar attributen för pennstreck i enlighet med detta.

// Update ink stroke color for new strokes.
private void OnPenColorChanged(object sender, SelectionChangedEventArgs e)
{
    if (inkCanvas != null)
    {
        InkDrawingAttributes drawingAttributes =
            inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();

        string value = ((ComboBoxItem)PenColor.SelectedItem).Content.ToString();

        switch (value)
        {
            case "Black":
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
            case "Red":
                drawingAttributes.Color = Windows.UI.Colors.Red;
                break;
            default:
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
        };

        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    }
}

Dessa bilder visar hur pennindata bearbetas och anpassas av InkPresenter.

Skärmbild som visar InkCanvas med svarta pennstreck som standard.

InkCanvas med svarta pennstreck som standard.

Skärmbild av InkCanvas med användaren markerade röda pennstreck.

InkCanvas med användarvalda röda pennstreck.

För att tillhandahålla funktioner utöver pennanteckning och radering, till exempel linjeval, måste din app identifiera specifika indata för att InkPresenter ska kunna gå igenom obearbetat för hantering av din app.

Direktindata för avancerad bearbetning

Som standard bearbetar InkPresenter alla indata som antingen ett pennstreck eller ett raderingsdrag, inklusive indata som ändrats av en sekundär maskinvaruuppgång, till exempel en penntunnaknapp, en höger musknapp eller liknande. Användare förväntar sig vanligtvis dock vissa ytterligare funktioner eller ändrade beteenden med dessa sekundära möjligheter.

I vissa fall kan du också behöva tillhandahålla ytterligare funktionalitet för pennor utan sekundära funktioner (funktioner som vanligtvis inte är kopplade till pennspetsen), andra typer av inmatningsenheter, eller någon typ av ändrat beteende baserat på ett användarval i appens användargränssnitt.

För att stödja detta kan InkPresenter konfigureras för att lämna specifika indata obearbetade. Den här obearbetade indata skickas sedan till din app för bearbetning.

Exempel – Användning av obearbetad indata för att implementera linjeval

Windows Ink-plattformen ger inte inbyggt stöd för åtgärder som kräver ändrade indata, till exempel linjeval. Om du vill ha stöd för funktioner som denna måste du tillhandahålla en anpassad lösning i dina appar.

Följande kodexempel (all kod finns i MainPage.xaml- och MainPage.xaml.cs-filer) steg för steg hur du aktiverar linjeval när indata ändras med en penntunnaknapp (eller höger musknapp).

  1. Först konfigurerar vi användargränssnittet i MainPage.xaml.

    Här lägger vi till en arbetsyta (under InkCanvas) för att rita markeringsstrecket. Om du använder ett separat lager för att rita markeringsstrecket blir InkCanvas och dess innehåll orört.

    Skärmbild av den tomma InkCanvas med en underliggande markeringsyta.

      <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.RowDefinitions>
          <RowDefinition Height="Auto"/>
          <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
          <TextBlock x:Name="Header"
            Text="Advanced ink customization sample"
            VerticalAlignment="Center"
            Style="{ThemeResource HeaderTextBlockStyle}"
            Margin="10,0,0,0" />
        </StackPanel>
        <Grid Grid.Row="1">
          <!-- Canvas for displaying selection UI. -->
          <Canvas x:Name="selectionCanvas"/>
          <!-- Inking area -->
          <InkCanvas x:Name="inkCanvas"/>
        </Grid>
      </Grid>
    
  2. I MainPage.xaml.cs deklarerar vi ett par globala variabler för att behålla referenser till aspekter av urvalsgränssnittet. Mer specifikt markeringslasso-strecket och den avgränsningsrektangel som markerar de markerade linjerna.

      // Stroke selection tool.
      private Polyline lasso;
      // Stroke selection area.
      private Rect boundingRect;
    
  3. Därefter konfigurerar vi InkPresenter för att tolka indata från både pennan och musen som pennstreck och anger några inledande pennstrecksattribut som används för återgivningsstreck till InkCanvas.

    Viktigast av allt är att vi använder egenskapen InputProcessingConfiguration för InkPresenter för att ange att eventuella ändrade indata ska bearbetas av appen. Ändrade indata anges genom att tilldela InputProcessingConfiguration.RightDragAction värdet InkInputRightDragAction.LeaveUnprocessed. När det här värdet har angetts går InkPresenter vidare till klassen InkUnprocessedInput , en uppsättning pekarhändelser som du kan hantera.

    Vi tilldelar lyssnare för de obearbetade PointerPressed-, PointerMoved- och PointerReleased-händelserna som skickas av InkPresenter. Alla urvalsfunktioner implementeras i hanterarna för dessa händelser.

    Slutligen tilldelar vi lyssnare för händelserna StrokeStarted och StrokesErased i InkPresenter. Vi använder händelsehanterare för dessa händelser för att rensa markeringsgränssnittet när en ny linje startas eller en befintlig linje raderas.

    Skärmbild av exempelappen Advances ink customization som visar inkcanvas med svarta pennstreck som standard.

      public MainPage()
      {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
          Windows.UI.Core.CoreInputDeviceTypes.Mouse |
          Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
        // Set initial ink stroke attributes.
        InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
        drawingAttributes.Color = Windows.UI.Colors.Black;
        drawingAttributes.IgnorePressure = false;
        drawingAttributes.FitToCurve = true;
        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
        // By default, the InkPresenter processes input modified by
        // a secondary affordance (pen barrel button, right mouse
        // button, or similar) as ink.
        // To pass through modified input to the app for custom processing
        // on the app UI thread instead of the background ink thread, set
        // InputProcessingConfiguration.RightDragAction to LeaveUnprocessed.
        inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
            InkInputRightDragAction.LeaveUnprocessed;
    
        // Listen for unprocessed pointer events from modified input.
        // The input is used to provide selection functionality.
        inkCanvas.InkPresenter.UnprocessedInput.PointerPressed +=
            UnprocessedInput_PointerPressed;
        inkCanvas.InkPresenter.UnprocessedInput.PointerMoved +=
            UnprocessedInput_PointerMoved;
        inkCanvas.InkPresenter.UnprocessedInput.PointerReleased +=
            UnprocessedInput_PointerReleased;
    
        // Listen for new ink or erase strokes to clean up selection UI.
        inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
            StrokeInput_StrokeStarted;
        inkCanvas.InkPresenter.StrokesErased +=
            InkPresenter_StrokesErased;
      }
    
  4. Sedan definierar vi hanterare för de obearbetade PointerPressed-, PointerMoved- och PointerReleased-händelserna som skickas av InkPresenter.

    Alla urvalsfunktioner implementeras i dessa hanterare, inklusive lasso-stråket och rektangeln för avgränsning.

    Skärmbild av markeringslasso.

      // Handle unprocessed pointer events from modified input.
      // The input is used to provide selection functionality.
      // Selection UI is drawn on a canvas under the InkCanvas.
      private void UnprocessedInput_PointerPressed(
        InkUnprocessedInput sender, PointerEventArgs args)
      {
        // Initialize a selection lasso.
        lasso = new Polyline()
        {
            Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
            StrokeThickness = 1,
            StrokeDashArray = new DoubleCollection() { 5, 2 },
            };
    
            lasso.Points.Add(args.CurrentPoint.RawPosition);
    
            selectionCanvas.Children.Add(lasso);
        }
    
        private void UnprocessedInput_PointerMoved(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add a point to the lasso Polyline object.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
        }
    
        private void UnprocessedInput_PointerReleased(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add the final point to the Polyline object and
          // select strokes within the lasso area.
          // Draw a bounding box on the selection canvas
          // around the selected ink strokes.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
    
          boundingRect =
            inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
              lasso.Points);
    
          DrawBoundingRect();
        }
    
  5. För att avsluta händelsehanteraren PointerReleased rensar vi markeringslagret för allt innehåll (lasso-strecket) och ritar sedan en enda avgränsningsrektangel runt pennstrecken som omfattas av lasso-området.

    Skärmbild av markeringsavgränsningsrektan.

      // Draw a bounding rectangle, on the selection canvas, encompassing
      // all ink strokes within the lasso area.
      private void DrawBoundingRect()
      {
        // Clear all existing content from the selection canvas.
        selectionCanvas.Children.Clear();
    
        // Draw a bounding rectangle only if there are ink strokes
        // within the lasso area.
        if (!((boundingRect.Width == 0) ||
          (boundingRect.Height == 0) ||
          boundingRect.IsEmpty))
          {
            var rectangle = new Rectangle()
            {
              Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
                StrokeThickness = 1,
                StrokeDashArray = new DoubleCollection() { 5, 2 },
                Width = boundingRect.Width,
                Height = boundingRect.Height
            };
    
            Canvas.SetLeft(rectangle, boundingRect.X);
            Canvas.SetTop(rectangle, boundingRect.Y);
    
            selectionCanvas.Children.Add(rectangle);
          }
        }
    
  6. Slutligen definierar vi hanterare för händelserna StrokeStarted och StrokesErased InkPresenter.

    Båda anropar samma rensningsfunktion för att rensa den aktuella markeringen när ett nytt streck detekteras.

      // Handle new ink or erase strokes to clean up selection UI.
      private void StrokeInput_StrokeStarted(
        InkStrokeInput sender, Windows.UI.Core.PointerEventArgs args)
      {
        ClearSelection();
      }
    
      private void InkPresenter_StrokesErased(
        InkPresenter sender, InkStrokesErasedEventArgs args)
      {
        ClearSelection();
      }
    
  7. Här är funktionen för att ta bort allt markeringsanvändargränssnitt från markeringsduken när en ny linje startas eller en befintlig linje raderas.

      // Clean up selection UI.
      private void ClearSelection()
      {
        var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
        foreach (var stroke in strokes)
        {
          stroke.Selected = false;
        }
        ClearDrawnBoundingRect();
       }
    
      private void ClearDrawnBoundingRect()
      {
        if (selectionCanvas.Children.Any())
        {
          selectionCanvas.Children.Clear();
          boundingRect = Rect.Empty;
        }
      }
    

Anpassad pennanteckningsåtergivning

Som standard bearbetas bläckindata på en bakgrundstråd med låg svarstid och renderas kontinuerligt eller "vått" när det ritas. När strecket är klart (pennan eller fingret lyfts eller musknappen släpps) bearbetas strecket på användargränssnittstråden och återges "torrt" till InkCanvas-lagret (ovanför programinnehållet och ersätter den våta pennanteckningen).

Du kan åsidosätta det här standardbeteendet och fullständigt kontrollera inkupplevelsen genom "anpassad torkning" av de våta bläckstrecken. Standardbeteendet räcker vanligtvis för de flesta program, men det finns några fall där anpassad torkning kan krävas, men följande är:

  • Effektivare hantering av stora eller komplexa samlingar med pennstreck
  • Mer effektivt stöd för panorering och zoomning på stora pennarbetsytor
  • Mellanlagring av pennanteckningar och andra objekt, till exempel former eller text, samtidigt som z-ordning bibehålls
  • Torka och konvertera pennanteckningar synkront till en DirectX-form (till exempel en rät linje eller form som rastreras och integreras i programinnehåll i stället för som ett separat InkCanvas-lager ).

Anpassad torkning kräver ett IInkD2DRenderer-objekt för att hantera pennanteckningarna och återge det till Direct2D-enhetskontexten för din Universella Windows-app i stället för standardkontrollen InkCanvas .

Genom att anropa ActivateCustomDrying (innan InkCanvas läses in) skapar en app ett InkSynchronizer-objekt för att anpassa hur ett pennstreck återges torrt till en SurfaceImageSource eller VirtualSurfaceImageSource.

Både SurfaceImageSource och VirtualSurfaceImageSource tillhandahåller en Delad DirectX-yta för din app att dra till och skapa i programmets innehåll, även om VSIS har en virtuell yta som är större än skärmen för att utföra panorering och zoomning. Eftersom visuella uppdateringar av dessa ytor synkroniseras med XAML UI-tråden, kan den våta bläcket tas bort från InkCanvas i samma ögonblick som bläck renderas till någon av dem.

Du kan också anpassa torr pennanteckning till en SwapChainPanel, men synkronisering med användargränssnittstråden är inte garanterad och det kan uppstå en fördröjning mellan när pennanteckningen återges till din SwapChainPanel och när pennanteckningen tas bort från InkCanvas.

Ett fullständigt exempel på den här funktionen finns i exemplet med komplex pennanteckning.

Anmärkning

Anpassad torkning och InkToolbar
Om din app åsidosätter standardbeteendet för inkrendering av InkPresenter med en anpassad torkningsimplementering är de renderade pennstrecken inte längre tillgängliga för InkToolbar och de inbyggda raderingskommandona i InkToolbar fungerar inte som förväntat. För att tillhandahålla raderingsfunktioner måste du hantera alla pekarhändelser, utföra träfftestning för varje slag och åsidosätta det inbyggda kommandot "Radera alla pennanteckningar".

Ämne Description
Identifiera bläckstreck Konvertera pennstreck till text med handskriftsigenkänning eller till former med anpassad igenkänning.
Lagra och hämta pennstreck Lagra pennstrecksdata i en GIF-fil (Graphics Interchange Format) med hjälp av ISF-metadata (Embedded Ink Serialized Format).
Lägga till en InkToolbar i en Windows-pennanteckningsapp Lägg till en standard InkToolbar i en inkg app för Windows, lägg till en anpassad pennknapp i InkToolbar och binda denna anpassade pennknapp till en anpassad penndefinition.

API:er

Samples

Arkivera exempel