Поделиться через


Пример буфера обмена для рукописного ввода

В этой программе показано, как копировать и вставлять рукописный ввод в другое приложение. Он также позволяет пользователю скопировать выделенный фрагмент росчерков и вставить результат в существующий объект рукописного ввода.

Доступны следующие режимы буфера обмена:

  • Сериализованный формат рукописного ввода (ISF)
  • Метафайл
  • EMF (Enhanced Metafile —расширенный метафайл)
  • Bitmap
  • Рукописный ввод текста
  • Рисование рукописного ввода

Рукописный ввод текста и рукописный набросок — это два типа элементов управления рукописным вводом, которые используются в качестве текста или рисования соответственно. В существующие рукописные фрагменты можно вставить ISF, текстовые рукописные фрагменты и наброски.

В дополнение к буферу обмена в этом примере также показано, как выбирать штрихи с помощью инструмента лассо. Пользователь может перемещать выбранные штрихи и изменять их атрибуты рисования. Эта функция представляет собой подмножество функций выбора, уже предоставляемых элементом управления наложения рукописного ввода; Он реализуется здесь для наглядности.

В этом примере используются следующие функции:

  • Объект InkCollector .
  • Поддержка буфера обмена рукописного ввода.
  • Использование лассо с методом Microsoft.Ink.Ink.HitTest .

В этом примере показана отрисовка рукописного ввода, копирование этого рукописного фрагмента, а затем вставка рукописного ввода в другое приложение, например Microsoft Paint.

Сбор рукописного ввода и настройка формы

Сначала укажите интерфейсы автоматизации планшетного ПК, которые устанавливаются с помощью пакета SDK для сущности Microsoft Windows<type="reg"/> XP Tablet PC Edition.

using Microsoft.Ink;

Далее форма объявляет некоторые константы и поля, которые будут отмечены далее в этом примере.

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

Наконец, в обработчике событий Load формы инициализируется форма, создается объект InkCollector для формы и включается сборщик рукописного ввода.

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

Обработка событий меню

Обработчики событий пункта меню в основном обновляют состояние формы.

Команда Очистить удаляет прямоугольник выделения и штрихи из объекта Ink сборщика рукописного ввода.

Команда Exit отключает сборщик рукописного ввода перед выходом из приложения.

Меню Правка включает команды Вырезать и Копировать в зависимости от состояния выбора формы, а также команду Вставить на основе содержимого буфера обмена, определяемого с помощью метода CanPaste объекта Ink.

Команды Вырезать и Копировать используют вспомогательный метод для копирования рукописного ввода в буфер обмена. Команда Вырезать использует вспомогательный метод для удаления выделенных штрихов.

Команда Paste сначала проверяет метод CanPaste объекта Ink, чтобы узнать, можно ли вставить объект в буфер обмена. Затем команда Вставить вычисляет левый верхний угол области вставки, преобразует координаты из пикселей в пространство рукописного ввода и вставляет штрихи из буфера обмена в сборщик рукописного ввода. Наконец, поле выбора обновляется.

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

Команды Выбрать и Рукописный ввод обновляют режим приложения и атрибуты рисования по умолчанию, очищают текущий выбор, обновляют состояние меню и обновляют форму. Другие обработчики зависят от состояния приложения для выполнения правильной функции, при этом выполняется лассо или ввод рукописного ввода. Кроме того, команда Select добавляет обработчики событий NewPackets и Stroke в сборщик рукописного ввода, а команда Ink удаляет эти обработчики событий из сборщика рукописного ввода.

Форматы, доступные в буфере обмена при копировании росчерков, перечислены в меню Формат, и пользователь выбирает формат для копирования рукописного ввода из этого списка. Доступные типы форматов: ink Serialized Format (ISF), metafile, enhanced metafile и bitmap. Форматы рукописного ввода наброска и текста являются взаимоисключающими и зависят от копирования рукописного ввода в буфер обмена в качестве объекта OLE.

Меню Стиль позволяет пользователю изменять свойства цвета и ширины пера и всех выбранных штрихов.

Например, команда Red задает для свойства Color свойства DefaultDrawingAttributes сборщика рукописного ввода красный цвет. Так как свойство DrawingAttributes объекта Cursor не задано, любой новый рукописный ввод, нарисованный в сборщике рукописного ввода, наследуется цветом рисунка по умолчанию. Кроме того, если в данный момент выбраны какие-либо штрихи, свойство Color также обновляется для атрибутов рисования каждого росчерка.

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

Обработка событий мыши

Обработчик событий MouseMove проверяет режим приложения. Если режим MoveInk и кнопка мыши не работает, обработчик перемещает штрихи с помощью метода Move коллекции Strokes и обновляет поле выбора. В противном случае обработчик проверяет, содержит ли прямоугольник выбора курсор, включает соответствующий сбор рукописных фрагментов, а также устанавливает курсор соответствующим образом.

Обработчик событий MouseDown проверяет параметр курсора. Если для курсора задано значение SizeAll, обработчик устанавливает в режиме приложения значение MoveInk и записывает расположение курсора. В противном случае, если имеется текущий выбор, очистите его.

Обработчик событий MouseUp проверяет режим приложения. Если используется режим MoveInk, обработчик задает режим приложения на основе выбранного состояния команды Select.

Событие NewPackets возникает в режиме выбора, когда сборщик рукописного ввода получает новые данные пакета. Если приложение находится в режиме выбора, необходимо перехватить новые пакеты и использовать их для рисования фрагмента выделения.

Координата каждого пакета преобразуется в пиксели, ограничивается областью рисования и добавляется в коллекцию точек лассо. Затем вызывается вспомогательный метод для рисования лассо в форме.

Обработка нового росчерка

Событие Stroke возникает в режиме выделения при нарисовке нового росчерка. Если приложение находится в режиме выбора, этот штрих соответствует лассо и необходимо обновить сведения о выбранных штрихах.

Обработчик отменяет событие Stroke , проверяет наличие более двух точек лассо, копирует коллекцию Points в массив объектов Point и преобразует координаты точек в массиве из пикселей в пространство рукописного ввода. Затем обработчик использует метод HitTest объекта Ink, чтобы получить штрихи, выбранные точками лассо, и обновить состояние выделения формы. Наконец, росчерк, который вызвал событие, удаляется из коллекции выбранных штрихов, коллекция точек лассо очищается, а вспомогательный метод рисует прямоугольник выделения.

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

Копирование рукописного ввода в буфер обмена

Вспомогательный метод CopyInkToClipboard создает значение InkClipboardFormats, проверяет состояние меню Формат для обновления форматов, помещаемого в буфер обмена, и использует метод ClipboardCopy объекта Ink для копирования штрихов в буфер обмена.

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

Обновление выделенного фрагмента

Вспомогательный метод SetSelection обновляет поданные выбранные строки, и если коллекция имеет значение NULL или EMPTY, для прямоугольника выбора устанавливается пустой прямоугольник. Если выбранная коллекция Strokes не пуста, метод SetSelection выполняет следующие действия.

  • Определяет ограничивающий прямоугольник с помощью метода GetBoundingBox коллекции strokes.
  • Преобразует координаты прямоугольника из пространства рукописного ввода в пиксели
  • Надувает прямоугольник, чтобы предоставить некоторое визуальное пространство между ним и выбранными штрихами
  • Создает маркеры выделения для текущего поля выделения

Наконец, метод SetSelection задает видимость дескрипторов выделения и задает для свойства AutoRedraw сборщика рукописного ввода значение FALSE, если выбраны штрихи.

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

Рисование лассо

Лассо рисуется в виде ряда открытых точек, которые следуют за контуром росчерка лассо и пунктирной соединительной линии между двумя концами. Событие NewPackets возникает при рисовании лассо, а обработчик событий передает сведения об росчерке в метод DrawLasso.

Вспомогательный метод DrawLasso сначала удаляет старую соединительную линию, а затем выполняет итерацию по точкам в росчерке. Затем DrawLasso вычисляет место размещения точек вдоль росчерка и рисует их. Наконец, он рисует новую линию соединителя.

Закрытие формы

Метод Dispose формы удаляет объект InkCollector myInkCollector.