Freigeben über


Beispiel für Freihandablage

Dieses Programm veranschaulicht, wie Sie Freihandeingaben kopieren und in eine andere Anwendung einfügen. Außerdem kann der Benutzer eine Auswahl von Strichen kopieren und das Ergebnis in das vorhandene Freihandobjekt einfügen.

Die folgenden Zwischenablagemodi sind verfügbar:

  • Serialisiertes Freihandformat (ISF)
  • Metafile
  • Erweiterte Metadatei (Enhanced Metafile, EMF)
  • Bitmap
  • Freihandeingabe für Text
  • Skizzen in Freihand

Freihand- und Skizzeneingaben sind zwei Arten von Freihandsteuerelementen, die als Text bzw. Zeichnung verwendet werden. Es ist möglich, ISF, Texteingaben und Skizzen in vorhandene Freihandeingaben einzufügen.

Zusätzlich zur Zwischenablage wird in diesem Beispiel auch veranschaulicht, wie Striche mit dem Lassotool ausgewählt werden. Der Benutzer kann ausgewählte Striche verschieben und seine Zeichnungsattribute ändern. Diese Funktionalität ist eine Teilmenge der Auswahlfunktionalität, die bereits vom Ink-Overlay-Steuerelement bereitgestellt wird. es wird hier zu Veranschaulichungszwecken implementiert.

In diesem Beispiel werden die folgenden Features verwendet:

Dieses Beispiel veranschaulicht das Rendern von Freihandeingaben, das Kopieren dieser Freihandeingabe und das anschließende Einfügen der Freihandeingabe in eine andere Anwendung, z. B. Microsoft Paint.

Sammeln von Freihandeingaben und Einrichten des Formulars

Verweisen Sie zunächst auf die Tablet PC Automation-Schnittstellen, die mit dem Microsoft Windows<Entity type="reg"/> XP Tablet PC Edition Software Development Kit (SDK) installiert werden.

using Microsoft.Ink;

Als Nächstes deklariert das Formular einige Konstanten und Felder, die später in diesem Beispiel notiert werden.

// Declare constant for the size of the border around selected strokes
private const int SelectedInkWidthIncrease = 105;

// Declare constant for the size of a lasso point
private const int DotSize = 6;

// Declare constant for the spacing between lasso points
private const int DotSpacing = 7;

// Declare constant for the selection rectangle padding
private const int SelectionRectBuffer = 8;

// Declare constant for the lasso hit test percent (specifies how much
// of the stoke must fall within the lasso in order to be selected).
private const float LassoPercent = 50;
...
// Declare the InkCollector object
private InkCollector myInkCollector = null;

// The points in the selection lasso
private ArrayList lassoPoints = null;

// The array of rectangle selection handles
private PictureBox[] selectionHandles;

// The rectangle that bounds the selected strokes
private Rectangle selectionRect = Rectangle.Empty;

// The strokes that have been selected by the lasso
private Strokes selectedStrokes = null;
...
// Declare the colors used in the selection lasso
private Color dotEdgeColor = Color.White;
private Color dotColor = SystemColors.Highlight;
private Color connectorColor = Color.Black;

// Declare the pens used to draw the selection lasso
private Pen connectorPen = null;
private Pen dotEdgePen = null;
private Pen dotPen = null;

Schließlich wird im Load-Ereignishandler des Formulars das Formular initialisiert, ein InkCollector-Objekt für das Formular erstellt und der Freihandsammler aktiviert.

// Create an ink collector and assign it to this form's window
myInkCollector = new InkCollector(this.Handle);

// Turn the ink collector on
myInkCollector.Enabled = true;

Behandeln von Menüereignissen

Die Menüelement-Ereignishandler aktualisieren in erster Linie den Zustand des Formulars.

Der Befehl Clear entfernt das Auswahlrechteck und löscht die Striche aus dem Freihandobjekt des Freihandsammlers.

Der Befehl Exit deaktiviert den Freihandsammler, bevor die Anwendung beendet wird.

Das Menü Bearbeiten aktiviert die Befehle Ausschneiden und Kopieren basierend auf dem Auswahlzustand des Formulars sowie den Befehl Einfügen basierend auf dem Inhalt der Zwischenablage, der mithilfe der CanPaste-Methode des Freihandobjekts bestimmt wird.

Die Befehle Ausschneiden und Kopieren verwenden beide eine Hilfsmethode, um Freihandeingaben in die Zwischenablage zu kopieren. Der Befehl Cut verwendet eine Hilfsmethode, um die ausgewählten Striche zu löschen.

Der Befehl Einfügen überprüft zunächst die CanPaste-Methode des Freihandobjekts, um festzustellen, ob das Objekt in die Zwischenablage eingefügt werden kann. Der Befehl Einfügen berechnet dann die obere linke Ecke für den Einfügebereich, konvertiert die Koordinaten von Pixeln in Freihandraum und fügt die Striche aus der Zwischenablage in den Freihandsammler ein. Schließlich wird das Auswahlfeld aktualisiert.

if (myInkCollector.Ink.CanPaste())
{
   // Compute the location where the ink should be pasted;
    // this location should be shifted from the origin
    // to account for the width of the selection rectangle's handle.
    Point offset = new Point(leftTopHandle.Width+1,leftTopHandle.Height+1);
    using (Graphics g = CreateGraphics())
    {
        myInkCollector.Renderer.PixelToInkSpace(g, ref offset);
    }
    // Use Ink API to paste the clipboard data into the Ink
    Strokes pastedStrokes = myInkCollector.Ink.ClipboardPaste(offset);

    // If the contents of the clipboard were a valid format 
    // (Ink Serialized Format or Embeddable OLE Object) and at
    // least one stroke was pasted into the ink, use a helper 
    // method to update the stroke selection.  Otherwise,
    // the result is null and this paste becomes a no-op.  
    if (null != pastedStrokes)
    {
        SetSelection(pastedStrokes);
    }
}

Mit den Befehlen Select und Ink werden der Anwendungsmodus und die Standardzeichnungsattribute aktualisiert, die aktuelle Auswahl gelöscht, der Menüzustand aktualisiert und das Formular aktualisiert. Andere Handler verlassen sich auf den Anwendungszustand, um die richtige Funktion auszuführen, entweder lassoing oder freihand legen. Darüber hinaus fügt der Befehl Select dem Freihandsammler die Ereignishandler NewPackets und Stroke hinzu, und der Ink-Befehl entfernt diese Ereignishandler aus dem Freihandsammler.

Die Formate, die beim Kopieren von Strichen in der Zwischenablage verfügbar sind, werden im Menü Format aufgeführt, und der Benutzer wählt das Format zum Kopieren der Freihandeingabe aus dieser Liste aus. Zu den verfügbaren Formattypen gehören Ink Serialized Format (ISF), Metadatei, erweiterte Metadatei und Bitmap. Die Freihand- und Text-Freihandformate schließen sich gegenseitig aus und basieren darauf, dass die Freihandeingabe als OLE-Objekt in die Zwischenablage kopiert wird.

Über das Menü Formatvorlage kann der Benutzer die Farb- und Breiteneigenschaften des Stifts und aller ausgewählten Striche ändern.

Der Befehl Red legt beispielsweise die Color-Eigenschaft der DefaultDrawingAttributes-Eigenschaft des Freihandsammlers auf die Farbe Rot fest. Da die DrawingAttributes-Eigenschaft des Cursor-Objekts nicht festgelegt wurde, erbt jede neue Freihandeingabe, die auf den Freihandsammler gezeichnet wird, auf die Standardzeichnungsfarbe. Wenn derzeit Striche ausgewählt sind, werden auch die Zeichnungsattribute der einzelnen Striche color-Eigenschaft aktualisiert.

private void SetColor(Color newColor)
{
    myInkCollector.DefaultDrawingAttributes.Color = newColor;

    // In addition to updating the ink collector, also update
    // the drawing attributes of all selected strokes.
    if (HasSelection())
    {
        foreach (Stroke s in selectedStrokes)
        {
            s.DrawingAttributes.Color = newColor;
        }
    }

    Refresh();
}

Behandeln von Mausereignissen

Der MouseMove-Ereignishandler überprüft den Anwendungsmodus. Wenn der Modus MoveInk ist und eine Maustaste ausgefallen ist, verschiebt der Handler die Striche mithilfe der Move-Methode der Strokes-Auflistung und aktualisiert das Auswahlfeld. Andernfalls überprüft der Handler, ob das Auswahlrechteck den Cursor enthält, aktiviert die Freihandsammlung entsprechend und legt den Cursor entsprechend fest.

Der MouseDown-Ereignishandler überprüft die Cursoreinstellung. Wenn der Cursor auf SizeAll festgelegt ist, legt der Handler den Anwendungsmodus auf MoveInk fest und zeichnet die Cursorposition auf. Wenn eine aktuelle Auswahl vorhanden ist, löschen Sie sie andernfalls.

Der MouseUp-Ereignishandler überprüft den Anwendungsmodus. Wenn der Modus MoveInk ist, legt der Handler den Anwendungsmodus basierend auf dem aktivierten Zustand des Select-Befehls fest.

Das NewPackets-Ereignis wird im Auswahlmodus ausgelöst, wenn der Freihandsammler neue Paketdaten empfängt. Wenn sich die Anwendung im Auswahlmodus befindet, müssen die neuen Pakete abgefangen und zum Zeichnen des Auswahl lasso verwendet werden.

Die Koordinate jedes Pakets wird in Pixel konvertiert, auf den Zeichenbereich beschränkt und der Punktesammlung des Lassos hinzugefügt. Anschließend wird eine Hilfsmethode aufgerufen, um das Lasso auf dem Formular zu zeichnen.

Behandeln eines neuen Strichs

Das Stroke-Ereignis wird im Auswahlmodus ausgelöst, wenn ein neuer Strich gezeichnet wird. Wenn sich die Anwendung im Auswahlmodus befindet, entspricht dieser Strich dem Lasso, und es ist erforderlich, die Informationen der ausgewählten Striche zu aktualisieren.

Der Handler bricht das Stroke-Ereignis ab, sucht nach mehr als zwei Lassopunkten, kopiert die Points-Auflistung in ein Array von Point-Objekten und konvertiert die Koordinaten der Punkte im Array von Pixeln in freihandraum. Anschließend verwendet der Handler die HitTest-Methode des Ink-Objekts, um die von den Lassopunkten ausgewählten Striche abzurufen und den Auswahlstatus des Formulars zu aktualisieren. Schließlich wird der Strich, der das Ereignis ausgelöst hat, aus der Auflistung ausgewählter Striche entfernt, die Lassopunkte-Auflistung wird geleert, und eine Hilfsmethode zeichnet das Auswahlrechteck.

// This stroke corresponds to the lasso - 
// cancel it so that it is not added into the ink
e.Cancel = true;  

Strokes hitStrokes = null;

// If there are enough lasso points, perform a hit test
// to determine which strokes were selected. 
if (lassoPoints.Count > 2)
{

    // Convert the lasso points from pixels to ink space
    Point[] inkLassoPoints = (Point[])lassoPoints.ToArray(typeof(Point));
    using (Graphics g = CreateGraphics())
    {
        myInkCollector.Renderer.PixelToInkSpace(g, ref inkLassoPoints);
    }
    // Perform a hit test on this ink collector's ink to
    // determine which points were selected by the lasso stroke.
    //
    // Note that there is a slight inefficiency here since the
    // lasso stroke is part of the ink and, therefore, part of the
    // hit test - even though we don't need it.   It would have 
    // been more efficient to remove the stroke from the ink before 
    // calling HitTest.  However, it is not good practice to modify 
    // the stroke inside of its own event handler.
    hitStrokes = myInkCollector.Ink.HitTest(inkLassoPoints, LassoPercent);
    hitStrokes.Remove(e.Stroke);
}

// Reset the lasso points
lassoPoints.Clear();
lastDrawnLassoDot = Point.Empty;

// Use helper method to set the selection
SetSelection(hitStrokes);

Kopieren von Freihand in die Zwischenablage

Die CopyInkToClipboard-Hilfsmethode erstellt einen InkClipboardFormats-Wert, überprüft den Status des Menüs Format, um die Formate zu aktualisieren, die in die Zwischenablage eingefügt werden sollen, und verwendet die ClipboardCopy-Methode des Freihandobjekts, um die Striche in die Zwischenablage zu kopieren.

// Declare the ink clipboard formats to put on the clipboard
InkClipboardFormats formats = new InkClipboardFormats();

// Use selected format menu items to set the clipboard 
// formats
...

// If at least one format was selected, invoke the Ink
// API's ClipboardCopy method.  Note that selectedStrokes
// could be null, but that this is ok - if selectedStrokes
// is null, all of the ink is copied.
if (formats != InkClipboardFormats.None)
{
    myInkCollector.Ink.ClipboardCopy(selectedStrokes,formats,clipboardModes);
}
else
{
    MessageBox.Show("No clipboard formats selected");
}

Aktualisieren einer Auswahl

Die SetSelection-Hilfsmethode aktualisiert die gespeicherten selectedStrokes, und wenn die Auflistung NULL oder EMPTY ist, wird das Auswahlrechteck auf das leere Rechteck festgelegt. Wenn die ausgewählte Strokes-Auflistung nicht leer ist, führt die SetSelection-Methode die folgenden Schritte aus:

  • Bestimmt das umgebende Rechteck mithilfe der GetBoundingBox-Methode der Strokes-Auflistung.
  • Konvertiert die Rechteckkoordinaten vom Freihandbereich in Pixel.
  • Vergrößert das Rechteck, um einen visuellen Abstand zwischen dem Rechteck und den ausgewählten Strichen bereitzustellen.
  • Erstellt Auswahlhandles für das aktuelle Auswahlfeld

Schließlich legt die SetSelection-Methode die Sichtbarkeit der Auswahlhandles fest und legt die AutoRedraw-Eigenschaft des Freihandsammlers auf FALSE fest, wenn Striche ausgewählt sind.

// Tracks whether the rectangle that bounds the selected
// strokes should be displayed
bool isSelectionVisible = false;

// Update the selected strokes collection
selectedStrokes = strokes;

// If no strokes are selected, set the selection rectangle
// to empty
if (!HasSelection())
{
    selectionRect = Rectangle.Empty;
}
    // Otherwise, at least one stroke is selected and it is necessary
    // to display the selection rectangle.
else
{
    isSelectionVisible = true;

    // Retrieve the bounding box of the strokes
    selectionRect = selectedStrokes.GetBoundingBox();
    using (Graphics g = CreateGraphics())
    {
        InkSpaceToPixel(g, ref selectionRect);
    }

    // Pad the selection rectangle so that the selected ink 
    // doesn't overlap with the selection rectangle's handles.
    selectionRect.Inflate(SelectionRectBuffer, SelectionRectBuffer);

    // compute the center of the rectangle that bounds the 
    // selected strokes
    int xAvg = (selectionRect.Right+selectionRect.Left)/2;
    int yAvg = (selectionRect.Top+selectionRect.Bottom)/2;

    // Draw the resize handles
    // top left
    SetLocation(selectionHandles[0],selectionRect.Left, selectionRect.Top);
    // top
    SetLocation(selectionHandles[1],xAvg, selectionRect.Top);
    // top right 
    SetLocation(selectionHandles[2],selectionRect.Right, selectionRect.Top);

    // left 
    SetLocation(selectionHandles[3],selectionRect.Left, yAvg);
    // right
    SetLocation(selectionHandles[4],selectionRect.Right, yAvg);

    // bottom left
    SetLocation(selectionHandles[5],selectionRect.Left, selectionRect.Bottom);
    // bottom
    SetLocation(selectionHandles[6],xAvg, selectionRect.Bottom);
    // bottom right
    SetLocation(selectionHandles[7],selectionRect.Right, selectionRect.Bottom);
}

// Set the visibility of each selection handle in the 
// selection rectangle.  If there is no selection, all 
// handles should be hidden.  Otherwise, all handles should
// be visible.
foreach(PictureBox pb in selectionHandles)
{
    pb.Visible = isSelectionVisible;
}

// Turn off autoredrawing if there is a selection - otherwise,
// the selected ink is not displayed as selected.
myInkCollector.AutoRedraw = !isSelectionVisible;

// Since the selection has changed, repaint the screen.
Refresh();

Zeichnen des Lassos

Das Lasso wird als eine Reihe von offenen Punkten gezeichnet, die dem Pfad des Lassostrichs und einer gestrichelten Verbindungslinie zwischen den beiden Enden folgen. Das NewPackets-Ereignis wird ausgelöst, wenn das Lasso gezeichnet wird, und der Ereignishandler übergibt die Strichinformationen an die DrawLasso-Methode.

Die DrawLasso-Hilfsmethode entfernt zunächst die alte Verbinderlinie und durchläuft dann die Punkte im Strich. Anschließend berechnet DrawLasso, wo die Punkte entlang des Strichs platziert werden sollen, und zeichnet sie. Schließlich zeichnet es eine neue Verbinderlinie.

Schließen des Formulars

Die Dispose-Methode des Formulars verwirf das InkCollector-Objekt myInkCollector.