Aggiungere un controllo InkToolbar a un'app per l'input penna UWP

Esistono due diversi controlli che facilitano l'input penna nelle app Windows: InkCanvas e InkToolbar.

Il controllo InkCanvas fornisce funzionalità di Windows Ink di base. Usarlo per eseguire il rendering dell'input penna come tratto input penna (usando le impostazioni predefinite per colore e spessore) o un tratto di cancellazione.

Per informazioni dettagliate sull'implementazione di InkCanvas, vedere Interazioni tramite penna e stilo nelle app Windows.

Come sovrimpressione completamente trasparente, InkCanvas non fornisce alcuna interfaccia utente predefinita per l'impostazione delle proprietà del tratto input penna. Se si vuole modificare l'esperienza di input penna predefinita, consentire agli utenti di impostare le proprietà del tratto input penna e di supportare altre funzionalità di input penna personalizzate, sono disponibili due opzioni:

  • Nel code-behind, usare l'oggetto InkPresenter sottostante associato a InkCanvas.

    Le API InkPresenter supportano una personalizzazione estesa dell'esperienza di input penna. Per altre informazioni, vedere Interazioni tramite penna e stilo nelle app Windows.

  • Associare un InkToolbar a InkCanvas. Per impostazione predefinita, InkToolbar offre una raccolta personalizzabile ed estendibile di pulsanti per l'attivazione di funzionalità correlate all'input penna, ad esempio dimensioni del tratto, colore dell'input penna e punta della penna.

    Questo argomento descrive InkToolbar.

API importanti: classe InkCanvas, classe InkToolbar, classe InkPresenter, Windows.UI.Input.Inking

InkToolbar predefinito

Per impostazione predefinita, InkToolbar include pulsanti per disegnare, cancellare, evidenziare e visualizzare uno stencil (righello o goniometro). A seconda della funzionalità, altre impostazioni e comandi, ad esempio colore dell'input penna, spessore del tratto, cancella l'input penna, vengono forniti in un riquadro a comparsa.

InkToolbar
Barra degli strumenti predefinita di Windows Ink

Per aggiungere un InkToolbar a un'app di input penna, posizionarlo sulla stessa pagina di InkCanvas e associare i due controlli.

  1. In MainPage.xaml dichiarare un oggetto contenitore (per questo esempio si usa un controllo Grid) per l'area di input penna.
  2. Dichiarare un oggetto InkCanvas come elemento figlio del contenitore. Le dimensioni di InkCanvas sono ereditate dal contenitore.
  3. Dichiarare un controllo InkToolbar e usare l'attributo TargetInkCanvas per associarlo a InkCanvas.

Nota

Verificare che InkToolbar sia dichiarato dopo InkCanvas. In caso contrario, la sovrimpressione InkCanvas rende inaccessibile InkToolbar.

<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" />
        <InkToolbar x:Name="inkToolbar"
          VerticalAlignment="Top"
          TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>

Personalizzazione di base

Questa sezione illustra alcuni scenari di personalizzazione della barra degli strumenti di Windows Ink di base.

Specificare la posizione e l'orientamento

Quando si aggiunge una barra degli strumenti di input penna all'app, è possibile accettare la posizione e l'orientamento predefiniti della barra degli strumenti o impostarli come richiesto dall'app o dall'utente.

XAML

Specificare in modo esplicito la posizione e l'orientamento della barra degli strumenti attraverso le proprietà VerticalAlignment, HorizontalAlignment e Orientation.

Default Esplicito
Default ink toolbar location and orientation Explicit ink toolbar location and orientation
Posizione e orientamento predefiniti della barra degli strumenti di Windows Ink Posizione e orientamento espliciti della barra degli strumenti di Windows Ink

Ecco il codice per impostare in modo esplicito la posizione e l'orientamento della barra degli strumenti input penna in XAML.

<InkToolbar x:Name="inkToolbar" 
    VerticalAlignment="Center" 
    HorizontalAlignment="Right" 
    Orientation="Vertical" 
    TargetInkCanvas="{x:Bind inkCanvas}" />

Inizializzare in base alle preferenze utente o allo stato del dispositivo

In alcuni casi, potrebbe essere necessario impostare la posizione e l'orientamento della barra degli strumenti di input penna in base alle preferenze utente o allo stato del dispositivo. L'esempio seguente mostra come impostare la posizione e l'orientamento della barra degli strumenti di input penna in base alle preferenze di scrittura (mano destra o sinistra) specificate in Impostazioni > Dispositivi > Penna & Windows Ink > Penna > Scegliere la mano con cui si scrive.

Dominant hand setting
Impostazione della mano dominante

È possibile eseguire una query su questa impostazione tramite la proprietà HandPreference di Windows.UI.ViewManagement e impostare HorizontalAlignment in base al valore restituito. In questo esempio la barra degli strumenti si trova sul lato sinistro dell'app per i mancini e sul lato destro per i destrimani.

Scaricare questo esempio dall'esempio sulla posizione e l'orientamento della barra degli strumenti dell'input penna (di base)

public MainPage()
{
    this.InitializeComponent();

    Windows.UI.ViewManagement.UISettings settings = 
        new Windows.UI.ViewManagement.UISettings();
    HorizontalAlignment alignment = 
        (settings.HandPreference == 
            Windows.UI.ViewManagement.HandPreference.LeftHanded) ? 
            HorizontalAlignment.Left : HorizontalAlignment.Right;
    inkToolbar.HorizontalAlignment = alignment;
}

Modificare dinamicamente lo stato dell'utente o del dispositivo

È anche possibile usare l'associazione per verificare gli aggiornamenti dell'interfaccia utente in base alle modifiche apportate alle preferenze utente, alle impostazioni o agli stati del dispositivo. Nell'esempio seguente si espande l'esempio precedente e si mostra come posizionare dinamicamente la barra degli strumenti input penna in base all'orientamento del dispositivo usando l'associazione, un oggetto ViewMOdel e l'interfaccia INotifyPropertyChanged.

Scaricare questo esempio dall'esempio sulla posizione e l'orientamento della barra degli strumenti dell'input penna (dinamico)

  1. Innanzitutto, si aggiunge il viewModel.

    1. Aggiungere una nuova cartella al progetto e chiamarla ViewModels.

    2. Aggiungere una nuova classe alla cartella ViewModels (per questo esempio si è chiamata InkToolbarSnippetHostViewModel.cs).

      Nota

      Si è usato il modello Singleton perché è necessario un solo oggetto di questo tipo per la durata dell'applicazione

    3. Aggiungere lo spazio dei nomi using System.ComponentModel al file.

    4. Aggiungere una variabile membro statica denominata instance e una proprietà di sola lettura statica denominata Instance. Rendere privato il costruttore per assicurarsi che questa classe sia accessibile solo tramite la proprietà Instance.

      Nota

      Questa classe eredita dall'interfaccia INotifyPropertyChanged, che viene usata per notificare ai client, in genere client di associazione, che un valore della proprietà è cambiato. Si userà per gestire le modifiche apportate all'orientamento del dispositivo (questo codice verrà espanso e illustrato più avanti in un passaggio successivo).

      using System.ComponentModel;
      
      namespace locationandorientation.ViewModels
      {
          public class InkToolbarSnippetHostViewModel : INotifyPropertyChanged
          {
              private static InkToolbarSnippetHostViewModel instance;
      
              public static InkToolbarSnippetHostViewModel Instance
              {
                  get
                  {
                      if (null == instance)
                      {
                          instance = new InkToolbarSnippetHostViewModel();
                      }
                      return instance;
                  }
              }
          }
      
          private InkToolbarSnippetHostViewModel() { }
      }
      
    5. Aggiungere due proprietà bool alla classe InkToolbarSnippetHostViewModel: LeftHandedLayout (stessa funzionalità dell'esempio solo XAML precedente) e PortraitLayout (orientamento del dispositivo).

      Nota

      La proprietà PortraitLayout è impostabile e include la definizione per l'evento PropertyChanged.

      public bool LeftHandedLayout
      {
          get
          {
              bool leftHandedLayout = false;
              Windows.UI.ViewManagement.UISettings settings =
                  new Windows.UI.ViewManagement.UISettings();
              leftHandedLayout = (settings.HandPreference ==
                  Windows.UI.ViewManagement.HandPreference.LeftHanded);
              return leftHandedLayout;
          }
      }
      
      public bool portraitLayout = false;
      public bool PortraitLayout
      {
          get
          {
              Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                  Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
              portraitLayout = 
                  (winOrientation == 
                      Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
              return portraitLayout;
          }
          set
          {
              if (value.Equals(portraitLayout)) return;
              portraitLayout = value;
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
          }
      }
      
  2. A questo punto, aggiungere un paio di classi convertitore al progetto. Ogni classe contiene un oggetto Convert che restituisce un valore di allineamento (HorizontalAlignment o VerticalAlignment).

    1. Aggiungere una nuova cartella al progetto e chiamarla Converters.

    2. Aggiungere due nuove classi alla cartella Converters (per questo esempio si chiamano HorizontalAlignmentFromHandednessConverter.cs e VerticalAlignmentFromAppViewConverter.cs).

    3. Aggiungere gli spazi dei nomi using Windows.UI.Xaml e using Windows.UI.Xaml.Data a ogni file.

    4. Modificare ogni classe in public e specificare che implementa l'interfaccia IValueConverter.

    5. Aggiungere i metodi Convert e ConvertBack a ogni file, come mostrato qui (si lascia il metodo ConvertBack non implementato).

      • HorizontalAlignmentFromHandednessConverter posiziona la barra degli strumenti input penna sul lato destro dell'app per i destrimani e sul lato sinistro per i mancini.
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class HorizontalAlignmentFromHandednessConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool leftHanded = (bool)value;
                  HorizontalAlignment alignment = HorizontalAlignment.Right;
                  if (leftHanded)
                  {
                      alignment = HorizontalAlignment.Left;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
      • VerticalAlignmentFromAppViewConverter posiziona la barra degli strumenti input penna al centro dell'app per l'orientamento verticale e nella parte superiore dell'app per l'orientamento orizzontale (sebbene si intenda migliorare l'usabilità, non è che una scelta arbitraria per scopi dimostrativi).
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class VerticalAlignmentFromAppViewConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool portraitOrientation = (bool)value;
                  VerticalAlignment alignment = VerticalAlignment.Top;
                  if (portraitOrientation)
                  {
                      alignment = VerticalAlignment.Center;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
  3. Aprire ora il file MainPage.xaml.cs.

    1. Aggiungere using using locationandorientation.ViewModels all'elenco degli spazi dei nomi per associare ViewModel.
    2. Aggiungere using Windows.UI.ViewManagement all'elenco degli spazi dei nomi per abilitare l'ascolto delle modifiche apportate all'orientamento del dispositivo.
    3. Aggiungere il codice WindowSizeChangedEventHandler.
    4. Impostare DataContext per la visualizzazione sull'istanza del modello singleton della classe InkToolbarSnippetHostViewModel.
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    using locationandorientation.ViewModels;
    using Windows.UI.ViewManagement;
    
    namespace locationandorientation
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
    
                Window.Current.SizeChanged += (sender, args) =>
                {
                    ApplicationView currentView = ApplicationView.GetForCurrentView();
    
                    if (currentView.Orientation == ApplicationViewOrientation.Landscape)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = false;
                    }
                    else if (currentView.Orientation == ApplicationViewOrientation.Portrait)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = true;
                    }
                };
    
                DataContext = InkToolbarSnippetHostViewModel.Instance;
            }
        }
    }
    
  4. Aprire ora il file MainPage.xaml.

    1. Aggiungere xmlns:converters="using:locationandorientation.Converters" all'elemento Page per l'associazione ai convertitori.

      <Page
      x:Class="locationandorientation.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:locationandorientation"
      xmlns:converters="using:locationandorientation.Converters"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
      
    2. Aggiungere un elemento PageResources e specificare i riferimenti ai convertitori.

      <Page.Resources>
          <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/>
          <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/>
      </Page.Resources>
      
    3. Aggiungere gli elementi InkCanvas e InkToolbar e associare le proprietà VerticalAlignment e HorizontalAlignment di InkToolbar.

      <InkCanvas x:Name="inkCanvas" />
      <InkToolbar x:Name="inkToolbar" 
                  VerticalAlignment="{Binding PortraitLayout, Converter={StaticResource VerticalAlignmentConverter} }" 
                  HorizontalAlignment="{Binding LeftHandedLayout, Converter={StaticResource HorizontalAlignmentConverter} }" 
                  Orientation="Vertical" 
                  TargetInkCanvas="{x:Bind inkCanvas}" />
      
  5. Tornare al file InkToolbarSnippetHostViewModel.cs per aggiungere le proprietà bool PortraitLayout e LeftHandedLayout alla classe InkToolbarSnippetHostViewModel, oltre al supporto per la riassociazione PortraitLayout quando il valore della proprietà cambia.

    public bool LeftHandedLayout
    {
        get
        {
            bool leftHandedLayout = false;
            Windows.UI.ViewManagement.UISettings settings =
                new Windows.UI.ViewManagement.UISettings();
            leftHandedLayout = (settings.HandPreference ==
                Windows.UI.ViewManagement.HandPreference.LeftHanded);
            return leftHandedLayout;
        }
    }
    
    public bool portraitLayout = false;
    public bool PortraitLayout
    {
        get
        {
            Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
            portraitLayout = 
                (winOrientation == 
                    Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
            return portraitLayout;
        }
        set
        {
            if (value.Equals(portraitLayout)) return;
            portraitLayout = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
        }
    }
    
    #region INotifyPropertyChanged Members
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
    
    #endregion
    

Ora si dobrebbe avere un'app di input penna che si adatta alla preferenza della mano dominante dell'utente e risponde dinamicamente all'orientamento del dispositivo dell'utente.

Specificare il pulsante selezionato

Pencil button selected at initialization
Barra degli strumenti di Windows Ink con il pulsante matita selezionato all'inizializzazione

Per impostazione predefinita, il primo pulsante (o il più a sinistra) viene selezionato all'avvio dell'app e all'inizializzazione della barra degli strumenti. Nella barra degli strumenti predefinita di Windows Ink, è il pulsante penna a sfera.

Poiché il framework definisce l'ordine dei pulsanti predefiniti, il primo pulsante potrebbe non essere la penna o lo strumento che si vuole attivare per impostazione predefinita.

È possibile eseguire l'override di questo comportamento predefinito e specificare il pulsante selezionato sulla barra degli strumenti.

Per questo esempio, si inizializza la barra degli strumenti predefinita con il pulsante matita selezionato e la matita attivata (anziché la penna a sfera).

  1. Usare la dichiarazione XAML per InkCanvas e InkToolbar dell'esempio precedente.
  2. Nel code-behind impostare un gestore per l'evento Loaded dell'oggetto InkToolbar.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loaded += inkToolbar_Loaded;
}
  1. Nel gestore per l'evento Loaded:

    1. Ottenere un riferimento all'oggetto InkToolbarPencilButton predefinito.

    Passando un oggetto InkToolbarTool.Pencil nel metodo GetToolButton si restituisce un oggetto InkToolbarToolButton per InkToolbarPencilButton.

    1. Impostare ActiveTool sull'oggetto restituito nel passaggio precedente.
/// <summary>
/// Handle the Loaded event of the InkToolbar.
/// By default, the active tool is set to the first tool on the toolbar.
/// Here, we set the active tool to the pencil button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void inkToolbar_Loaded(object sender, RoutedEventArgs e)
{
    InkToolbarToolButton pencilButton = inkToolbar.GetToolButton(InkToolbarTool.Pencil);
    inkToolbar.ActiveTool = pencilButton;
}

Specificare i pulsanti predefiniti

Specific buttons included at initialization
Pulsanti specifici inclusi all'inizializzazione

Come accennato, la barra degli strumenti di Windows Ink include una raccolta di pulsanti predefiniti. Questi pulsanti vengono visualizzati nell'ordine seguente (da sinistra a destra):

Per questo esempio, si inizializza la barra degli strumenti con i soli pulsanti predefiniti penna a sfera, matita e gomma.

È possibile eseguire questa operazione usando XAML o code-behind.

XAML

Modificare la dichiarazione XAML per InkCanvas e InkToolbar dal primo esempio.

Nota

I pulsanti vengono aggiunti alla barra degli strumenti nell'ordine definito dal framework, non l'ordine specificato qui.

<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" />
        <!-- Clear the default InkToolbar buttons by setting InitialControls to None. -->
        <!-- Set the active tool to the pencil button. -->
        <InkCanvas x:Name="inkCanvas" />
        <InkToolbar x:Name="inkToolbar"
                    VerticalAlignment="Top"
                    TargetInkCanvas="{x:Bind inkCanvas}"
                    InitialControls="None">
            <!--
             Add only the ballpoint pen, pencil, and eraser.
             Note that the buttons are added to the toolbar in the order
             defined by the framework, not the order we specify here.
            -->
            <InkToolbarEraserButton />
            <InkToolbarBallpointPenButton />
            <InkToolbarPencilButton/>
        </InkToolbar>
    </Grid>
</Grid>

Code-behind

  1. Usare la dichiarazione XAML per InkCanvas e InkToolbar dal primo esempio.
<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" />
        <InkToolbar x:Name="inkToolbar"
        VerticalAlignment="Top"
        TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>
  1. Nel code-behind impostare un gestore per l'evento Loading dell'oggetto InkToolbar.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loading += inkToolbar_Loading;
}
  1. Impostare InitialControls su "None".
  2. Creare riferimenti a oggetti per i pulsanti richiesti dall'app. Qui si aggiunge solo InkToolbarBallpointPenButton, InkToolbarPencilButton e InkToolbarEraserButton.

Nota

I pulsanti vengono aggiunti alla barra degli strumenti nell'ordine definito dal framework, non l'ordine specificato qui.

  1. Aggiungere i pulsanti a InkToolbar.
/// <summary>
/// Handles the Loading event of the InkToolbar.
/// Here, we identify the buttons to include on the InkToolbar.
/// </summary>
/// <param name="sender">The InkToolbar</param>
/// <param name="args">The InkToolbar event data.
/// If there is no event data, this parameter is null</param>
private void inkToolbar_Loading(FrameworkElement sender, object args)
{
    // Clear all built-in buttons from the InkToolbar.
    inkToolbar.InitialControls = InkToolbarInitialControls.None;

    // Add only the ballpoint pen, pencil, and eraser.
    // Note that the buttons are added to the toolbar in the order
    // defined by the framework, not the order we specify here.
    InkToolbarBallpointPenButton ballpoint = new InkToolbarBallpointPenButton();
    InkToolbarPencilButton pencil = new InkToolbarPencilButton();
    InkToolbarEraserButton eraser = new InkToolbarEraserButton();
    inkToolbar.Children.Add(eraser);
    inkToolbar.Children.Add(ballpoint);
    inkToolbar.Children.Add(pencil);
}

Pulsanti personalizzati e funzionalità di input penna

È possibile personalizzare ed estendere la raccolta di pulsanti (e le funzionalità di input penna associate) forniti tramite InkToolbar.

InkToolbar è costituito da due gruppi distinti di tipi di pulsanti:

  1. Gruppo di pulsanti "strumento" contenenti i pulsanti predefiniti di disegno, cancellazione ed evidenziazione. Qui si aggiungono penne e strumenti personalizzati.

Nota La selezione delle funzionalità comporta l'esclusione reciproca.

  1. Gruppo di pulsanti di attivazione/disattivazione contenenti il pulsante righello predefinito. Qui si aggiungono interruttori personalizzati.

NotaLe funzionalità non si escludono a vicenda e possono essere usate simultaneamente con altri strumenti attivi.

A seconda dell'applicazione e delle funzionalità di input penna necessarie, è possibile aggiungere uno dei pulsanti seguenti (associati alle funzionalità di input penna personalizzate) a InkToolbar:

  • Penna personalizzata: una penna per la quale la tavolozza dei colori input penna e le proprietà della punta della penna, ad esempio forma, rotazione e dimensioni, sono definite dall'app host.
  • Strumento personalizzato: uno strumento non penna, definito dall'app host.
  • Interruttore personalizzato: imposta lo stato di una funzionalità definita dall'app su attivato o disattivato. Quando è attivata, la funzionalità funziona insieme allo strumento attivo.

NotaNon è possibile modificare l'ordine di visualizzazione dei pulsanti predefiniti. L'ordine di visualizzazione predefinito è: penna, matita, evidenziatore, gomma e righello. Le penne personalizzate vengono aggiunte all'ultima penna predefinita, i pulsanti degli strumenti personalizzati vengono aggiunti tra l'ultimo pulsante della penna e il pulsante della gomma e i pulsanti di attivazione/disattivazione personalizzati vengono aggiunti dopo il pulsante del righello. I pulsanti personalizzati vengono aggiunti nell'ordine in cui sono specificati.

Penna personalizzata

È possibile creare una penna personalizzata (attivata tramite un pulsante penna personalizzato) in cui si definiscono la tavolozza dei colori input penna e le proprietà della punta della penna, ad esempio forma, rotazione e dimensioni.

Custom calligraphic pen button
Pulsante penna calligrafica personalizzata

Per questo esempio si definisce una penna personalizzata con una punta larga che consente tratti input penna calligrafici di base. Si personalizza anche la raccolta di pennelli nella tavolozza visualizzata nel riquadro a comparsa del pulsante.

Code-behind

Innanzitutto, si definite la penna personalizzata e si specificano gli attributi di disegno nel code-behind. Si farà riferimento a questa penna personalizzata da XAML in un secondo momento.

  1. Fare clic con il pulsante destro del mouse in Esplora soluzioni e selezionare Aggiungi -> Nuovo elemento.
  2. In Visual C# -> Codice aggiungere un nuovo file di classe e chiamarlo CalligraphicPen.cs.
  3. In Calligraphic.cs sostituire il blocco using predefinito con quanto segue:
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
  1. Specificare che la classe CalligraphicPen deriva da InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
  1. Eseguire l'override di CreateInkDrawingAttributesCore per specificare le dimensioni del pennello e del tratto.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
    }
}
  1. Creare un oggetto InkDrawingAttributes e impostare la forma della punta della penna, la rotazione della punta, le dimensioni del tratto e il colore dell'input penna.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
        InkDrawingAttributes inkDrawingAttributes =
          new InkDrawingAttributes();
        inkDrawingAttributes.PenTip = PenTipShape.Circle;
        inkDrawingAttributes.Size =
          new Windows.Foundation.Size(strokeWidth, strokeWidth * 20);
        SolidColorBrush solidColorBrush = brush as SolidColorBrush;
        if (solidColorBrush != null)
        {
            inkDrawingAttributes.Color = solidColorBrush.Color;
        }
        else
        {
            inkDrawingAttributes.Color = Colors.Black;
        }

        Matrix3x2 matrix = Matrix3x2.CreateRotation(45);
        inkDrawingAttributes.PenTipTransform = matrix;

        return inkDrawingAttributes;
    }
}

XAML

Si aggiungono quindi i riferimenti necessari alla penna personalizzata in MainPage.xaml.

  1. Si dichiara un dizionario risorsa pagina locale che crea un riferimento alla penna personalizzata (CalligraphicPen) definito in CalligraphicPen.cs e una raccolta di pennelli supportata dalla penna personalizzata (CalligraphicPenPalette).
<Page.Resources>
    <!-- Add the custom CalligraphicPen to the page resources. -->
    <local:CalligraphicPen x:Key="CalligraphicPen" />
    <!-- Specify the colors for the palette of the custom pen. -->
    <BrushCollection x:Key="CalligraphicPenPalette">
        <SolidColorBrush Color="Blue" />
        <SolidColorBrush Color="Red" />
    </BrushCollection>
</Page.Resources>
  1. Si aggiunge quindi InkToolbar con un elemento figlio InkToolbarCustomPenButton.

Il pulsante penna personalizzata include i due riferimenti alle risorse statiche dichiarate nelle risorse della pagina: CalligraphicPen e CalligraphicPenPalette.

Si specifica anche l'intervallo per il dispositivo di scorrimento delle dimensioni del tratto (MinStrokeWidth, MaxStrokeWidth e SelectedStrokeWidth), il pennello selezionato (SelectedBrushIndex) e l'icona per il pulsante penna personalizzato (SymbolIcon).

<Grid Grid.Row="1">
    <InkCanvas x:Name="inkCanvas" />
    <InkToolbar x:Name="inkToolbar"
                VerticalAlignment="Top"
                TargetInkCanvas="{x:Bind inkCanvas}">
        <InkToolbarCustomPenButton
            CustomPen="{StaticResource CalligraphicPen}"
            Palette="{StaticResource CalligraphicPenPalette}"
            MinStrokeWidth="1" MaxStrokeWidth="3" SelectedStrokeWidth="2"
            SelectedBrushIndex ="1">
            <SymbolIcon Symbol="Favorite" />
            <InkToolbarCustomPenButton.ConfigurationContent>
                <InkToolbarPenConfigurationControl />
            </InkToolbarCustomPenButton.ConfigurationContent>
        </InkToolbarCustomPenButton>
    </InkToolbar>
</Grid>

Interruttore personalizzato

È possibile creare un interruttore personalizzato (attivato tramite un pulsante di attivazione/disattivazione personalizzato) per impostare lo stato di una funzionalità definita dall'app su attivato o disattivato. Quando è attivata, la funzionalità funziona insieme allo strumento attivo.

In questo esempio si definisce un pulsante di attivazione/disattivazione personalizzato che consente l'input penna con input tocco (per impostazione predefinita, l'input penna tramite tocco non è abilitato).

Nota

Se è necessario supportare l'input penna con il tocco, è consigliabile abilitarlo usando customToggleButton, con l'icona e la descrizione comando specificati in questo esempio.

In genere, l'input tocco viene usato per la manipolazione diretta di un oggetto o dell'interfaccia utente dell'app. Per illustrare le differenze di comportamento quando l'input penna tramite tocco è abilitato, si inserisce InkCanvas all'interno di un contenitore ScrollViewer e si impostano le dimensioni di ScrollViewer in modo che siano inferiori a InkCanvas.

All'avvio dell'app è supportato solo l'input penna e il tocco viene usato per eseguire una panoramica o uno zoom della superficie di input penna. Quando l'input penna tramite tocco è abilitato, non è possibile eseguire la panoramica o lo zoom della superficie di input penna tramite input tocco.

Nota

Vedere le linee guida relative all'esperienza utente Controlli per l'input penna per InkCanvas e InkToolbar. I consigli seguenti sono rilevanti per questo esempio:

  • The InkToolbar, e l'input penna in generale, si usano meglio con una penna attiva. Tuttavia, l'input penna con mouse e tocco può essere supportato se richiesto dall'app.
  • Se si supporta l'input penna con l'input tocco, è consigliabile usare l'icona "ED5F" del tipo di carattere "Segoe MLD2 Assets" per l'interruttore, con una descrizione comando "Scrittura con tocco".

XAML

  1. Innanzitutto si dichiara un elemento InkToolbarCustomToggleButton (toggleButton) con un listener di eventi Click che specifica il gestore dell'evento (Toggle_Custom).
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" 
                x:Name="HeaderPanel" 
                Orientation="Horizontal">
        <TextBlock x:Name="Header" 
                   Text="Basic ink sample" 
                   Style="{ThemeResource HeaderTextBlockStyle}" 
                   Margin="10" />
    </StackPanel>

    <ScrollViewer Grid.Row="1" 
                  HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
        
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <InkToolbar Grid.Row="0" 
                        Margin="10"
                        x:Name="inkToolbar" 
                        VerticalAlignment="Top"
                        TargetInkCanvas="{x:Bind inkCanvas}">
                <InkToolbarCustomToggleButton 
                x:Name="toggleButton" 
                Click="CustomToggle_Click" 
                ToolTipService.ToolTip="Touch Writing">
                    <SymbolIcon Symbol="{x:Bind TouchWritingIcon}"/>
                </InkToolbarCustomToggleButton>
            </InkToolbar>
            
            <ScrollViewer Grid.Row="1" 
                          Height="500"
                          Width="500"
                          x:Name="scrollViewer" 
                          ZoomMode="Enabled" 
                          MinZoomFactor=".1" 
                          VerticalScrollMode="Enabled" 
                          VerticalScrollBarVisibility="Auto" 
                          HorizontalScrollMode="Enabled" 
                          HorizontalScrollBarVisibility="Auto">
                
                <Grid x:Name="outputGrid" 
                      Height="1000"
                      Width="1000"
                      Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
                    <InkCanvas x:Name="inkCanvas"/>
                </Grid>
                
            </ScrollViewer>
        </Grid>
    </ScrollViewer>
</Grid>

Code-behind

  1. Nel frammento di codice precedente si è dichiarato un listener di eventi Click e un gestore (Toggle_Custom) sul pulsante di attivazione/disattivazione personalizzato per l'input penna tramite tocco (toggleButton). Questo gestore attiva/disattiva il supporto per CoreInputDeviceTypes.Touch attraverso la proprietà InputDeviceTypes di InkPresenter.

    Si è specificata un'icona per il pulsante usando l'elemento SymbolIcon e l'estensione di markup {x:Bind} che lo associa a un campo definito nel file code-behind (TouchWritingIcon).

    Il frammento di codice seguente include sia il gestore dell'evento Click sia la definizione di TouchWritingIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomToggle : Page
    {
        Symbol TouchWritingIcon = (Symbol)0xED5F;

        public MainPage_AddCustomToggle()
        {
            this.InitializeComponent();
        }

        // Handler for the custom toggle button that enables touch inking.
        private void CustomToggle_Click(object sender, RoutedEventArgs e)
        {
            if (toggleButton.IsChecked == true)
            {
                inkCanvas.InkPresenter.InputDeviceTypes |= CoreInputDeviceTypes.Touch;
            }
            else
            {
                inkCanvas.InkPresenter.InputDeviceTypes &= ~CoreInputDeviceTypes.Touch;
            }
        }
    }
}

Strumento personalizzato

È possibile creare un pulsante strumento personalizzato per richiamare uno strumento non penna definito dall'app.

Per impostazione predefinita, InkPresenter elabora tutto l'input come tratto input penna o come tratto di cancellazione. È incluso l'input modificato da un invito hardware secondario, ad esempio un pulsante della penna, un pulsante destro del mouse o elemento simile. Tuttavia, è possibile configurare InkPresenter per lasciare input specifico non elaborato, per poterlo passare all'app per un'elaborazione personalizzata.

In questo esempio si definisce un pulsante strumento personalizzato che, se selezionato, determina l'elaborazione e il rendering dei tratti successivi come lasso di selezione (linea tratteggiata) anziché come input penna. Tutti i tratti input penna all'interno dei limiti dell'area di selezione sono impostati su Selezionato.

Nota

Vedere le linee guida relative all'esperienza utente Controlli per l'input penna per InkCanvas e InkToolbar. La raccomandazione seguente è rilevante per questo esempio:

  • Per la selezione del tratto,è consigliabile usare l'icona "EF20" del tipo di carattere "Segoe MLD2 Assets" per il pulsante dello strumento, con la descrizione comando "Selezione".

XAML

  1. Innanzitutto, si dichiara un elemento InkToolbarCustomToolButton (customToolButton) con un listener di eventi Click ch specifica il gestore dell'evento (customToolButton_Click) dove viene configurata la selezione del tratto. Si è anche aggiunto un set di pulsanti per copiare, tagliare e incollare la selezione del tratto.

  2. Si aggiunge anche un elemento Canvas per disegnare il tratto della selezione. L'uso di un livello separato per disegnare il tratto della selezione fa in modo che InkCanvas e il relativo contenuto rimangano inalterati.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <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>
    <StackPanel x:Name="ToolPanel" Orientation="Horizontal" Grid.Row="1">
        <InkToolbar x:Name="inkToolbar" 
                    VerticalAlignment="Top" 
                    TargetInkCanvas="{x:Bind inkCanvas}">
            <InkToolbarCustomToolButton 
                x:Name="customToolButton" 
                Click="customToolButton_Click" 
                ToolTipService.ToolTip="Selection tool">
                <SymbolIcon Symbol="{x:Bind SelectIcon}"/>
            </InkToolbarCustomToolButton>
        </InkToolbar>
        <Button x:Name="cutButton" 
                Content="Cut" 
                Click="cutButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="copyButton" 
                Content="Copy"  
                Click="copyButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="pasteButton" 
                Content="Paste"  
                Click="pasteButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
    </StackPanel>
    <Grid Grid.Row="2" x:Name="outputGrid" 
              Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}" 
              Height="Auto">
        <!-- Canvas for displaying selection UI. -->
        <Canvas x:Name="selectionCanvas"/>
        <!-- Canvas for displaying ink. -->
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

Code-behind

  1. Si gestisce quindi l'evento Click per InkToolbarCustomToolButton nel file code-behind MainPage.xaml.cs.

    Questo gestore configura InkPresenter per il passaggio di input non elaborato all'app.

    Per un passaggio più dettagliato di questo codice: vedere la sezione Input pass-through per l'elaborazione avanzata di Interazioni tramite penna e Windows Ink nelle app Windows.

    Si è specificata un'icona per il pulsante usando l'elemento SymbolIcon e l'estensione di markup {x:Bind} che lo associa a un campo definito nel file code-behind (SelectIcon).

    Il frammento di codice seguente include sia il gestore dell'evento Click sia la definizione di SelectIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomTool : Page
    {
        // Icon for custom selection tool button.
        Symbol SelectIcon = (Symbol)0xEF20;

        // Stroke selection tool.
        private Polyline lasso;
        // Stroke selection area.
        private Rect boundingRect;

        public MainPage_AddCustomTool()
        {
            this.InitializeComponent();

            // Listen for new ink or erase strokes to clean up selection UI.
            inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
                StrokeInput_StrokeStarted;
            inkCanvas.InkPresenter.StrokesErased +=
                InkPresenter_StrokesErased;
        }

        private void customToolButton_Click(object sender, RoutedEventArgs e)
        {
            // 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;
        }

        // 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();
        }

        private void cutButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
            inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
            ClearSelection();
        }

        private void copyButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
        }

        private void pasteButton_Click(object sender, RoutedEventArgs e)
        {
            if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
            {
                inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
                    new Point(0, 0));
            }
            else
            {
                // Cannot paste from clipboard.
            }
        }

        // Clean up selection UI.
        private void ClearSelection()
        {
            var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
            foreach (var stroke in strokes)
            {
                stroke.Selected = false;
            }
            ClearBoundingRect();
        }

        private void ClearBoundingRect()
        {
            if (selectionCanvas.Children.Any())
            {
                selectionCanvas.Children.Clear();
                boundingRect = Rect.Empty;
            }
        }

        // Handle unprocessed pointer events from modifed 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();
        }

        // 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);
            }
        }
    }
}

Personalizzare il rendering dell'input penna

Per impostazione predefinita, l'input penna viene elaborato in un thread in background a bassa latenza e sottoposto a rendering mentre viene diseganto (provvisorio). Quando il tratto viene completato (penna o dito sollevato o pulsante del mouse rilasciato), il tratto viene elaborato sul thread dell'interfaccia utente e sottoposto a rendering in modo definitivo sul livello InkCanvas (sopra il contenuto dell'applicazione e sostituendo l'input penna provvisorio).

La piattaforma input penna consente di eseguire l'override di questo comportamento e di personalizzare completamente l'esperienza di input penna rendendo definitivo l'input penna.

Per altre informazioni su come rendere definitivo l'input penna, vedere Interazioni tramite penna e Windows Ink nelle app Windows.

Nota

Rendering definitivo dell'input penna e InkToolbar
Se l'app esegue l'override del comportamento di rendering dell'input penna predefinito di InkPresenter con un'implementazione del rendering definitivo, i tratti input penna sottoposti a rendering non sono più disponibili per InkToolbar e i comandi di cancellazione predefiniti di InkToolbar non funzionano come previsto. Per fornire funzionalità di cancellazione, è necessario gestire tutti gli eventi del puntatore, eseguire hit test su ogni tratto ed eseguire l'override del comando "Cancella tutto l'input penna" predefinito.

Esempi di argomento

Altri esempi