墨迹剪贴板示例

此程序演示如何将墨迹复制并粘贴到另一个应用程序中。 它还使用户能够复制所选笔划并将结果粘贴到现有墨迹对象中。

以下剪贴板模式可用:

  • 墨迹序列化格式 (ISF)
  • Metafile
  • 增强型图元文件 (EMF)
  • Bitmap
  • 文本墨迹
  • 草图墨迹

文本墨迹和草图墨迹是两种类型的墨迹控件,分别用作文本或绘图。 可以将 ISF、文本墨迹和草图墨迹粘贴到现有墨迹中。

除了剪贴板,此示例还演示了如何使用套索工具选择笔划。 用户可以移动所选笔划并修改其绘图属性。 此功能是墨迹覆盖控件已提供的选择功能的子集;此处实现是为了说明目的。

此示例使用以下功能:

此示例演示了如何呈现墨迹、复制该墨迹,然后将墨迹粘贴到另一个应用程序(如Microsoft 画图)。

收集墨迹和设置表单

首先,参考 Tablet PC Automation 接口,这些接口随 Microsoft Windows<实体 type=“reg”/> XP Tablet PC Edition Software Development Kit (SDK) 一起安装。

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;

处理菜单事件

菜单项事件处理程序主要更新窗体的状态。

Clear 命令删除选择矩形,并从墨迹收集器的 Ink 对象中删除笔划。

Exit 命令在退出应用程序之前禁用墨迹收集器。

“编辑”菜单根据窗体的选择状态启用“剪切”和“复制”命令,并使用 Ink 对象的 CanPaste 方法确定,基于剪贴板的内容启用“粘贴”命令。

剪切和复制命令都使用帮助程序方法将墨迹复制到剪贴板。 “剪切”命令使用帮助程序方法删除所选笔划。

Paste 命令首先检查 Ink 对象的 CanPaste 方法,以查看是否可以粘贴剪贴板上的对象。 然后,Paste 命令计算粘贴区域的左上角,将坐标从像素转换为墨迹空间,并将笔划从剪贴板粘贴到墨迹收集器。 最后,更新选择框。

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 和 Ink 命令更新应用程序模式和默认绘图属性,清除当前所选内容,更新菜单状态,并刷新窗体。 其他处理程序依赖于应用程序状态来执行正确的功能,无论是套索还是放下墨迹。 此外,Select 命令将 NewPacketsStroke 事件处理程序添加到墨迹收集器,Ink 命令将从墨迹收集器中删除这些事件处理程序。

复制笔划时剪贴板上可用的格式列在“格式”菜单上,用户从此列表中选择用于复制墨迹的格式。 可用的格式类型包括墨迹序列化格式 (ISF) 、图元文件、增强型图元文件和位图。 草图墨迹和文本墨迹格式是互斥的,并且依赖于作为 OLE 对象复制到剪贴板的墨迹。

“样式”菜单使用户能够更改笔以及任何所选笔划的颜色和宽度属性。

例如,Red 命令将墨迹收集器的 DefaultDrawingAttributes 属性的 Color 属性设置为红色。 由于尚未设置 Cursor 对象的 DrawingAttributes 属性,因此绘制到墨迹收集器的任何新墨迹将继承为默认绘图颜色。 此外,如果当前选择了任何笔划,则还会更新每个笔划的绘图属性 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 且鼠标按钮已按下,则处理程序将使用 Strokes 集合的 Move 方法移动笔划并更新选择框。 否则,处理程序会检查以确定选择矩形是否包含光标、启用相应的墨迹收集,并相应地设置光标。

MouseDown 事件处理程序检查游标设置。 如果游标设置为 SizeAll,则处理程序会将应用程序模式设置为 MoveInk 并记录游标位置。 否则,如果有当前所选内容,请将其清除。

MouseUp 事件处理程序检查应用程序模式。 如果模式为 MoveInk,则处理程序会根据 Select 命令的选中状态设置应用程序模式。

当墨迹收集器接收新的数据包数据时,在选择模式下引发 NewPackets 事件。 如果应用程序处于选择模式,则需要截获新数据包并使用它们绘制选择套索。

每个数据包的坐标将转换为像素,限制为绘图区域,并添加到套索的点集合中。 然后调用帮助程序方法以在窗体上绘制套索。

处理新笔划

绘制新 笔划 时,在选择模式下引发 Stroke 事件。 如果应用程序处于选择模式,则此笔划对应于套索,并且需要更新所选笔划的信息。

处理程序取消 Stroke 事件,检查两个以上的套索点,将 Points 集合复制到 Point 对象的数组,并将数组中点的坐标从像素转换为墨迹空间。 然后,处理程序使用 Ink 对象的 HitTest 方法获取由套索点选择的笔划并更新窗体的选择状态。 最后,从所选笔划集合中删除引发事件的笔划,清空套索 Points 集合,并且帮助程序方法绘制选择矩形。

// 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 值,检查“格式”菜单的状态以更新要放入剪贴板的格式,并使用 Ink 对象的 ClipboardCopy 方法将笔划复制到剪贴板。

// 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 帮助程序方法更新 selectedStrokes 存档,如果集合为 NULLEMPTY,则选择矩形设置为空矩形。 如果所选的 Strokes 集合不为空,SetSelection 方法将执行以下步骤:

  • 使用笔划集合的 GetBoundingBox 方法确定边框
  • 将矩形坐标从墨迹空间转换为像素
  • 放大矩形,使其与所选笔划之间提供一些视觉空间
  • 为当前选择框创建选择句柄

最后,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。