此程式示範如何將筆跡複製並貼到另一個應用程式。 它也可讓使用者複製選擇的筆劃,並將結果貼入現有的墨跡物件中。
下列剪貼簿模式可供使用:
- 墨跡串行化格式 (ISF)
- 元檔
- 增強型元檔(EMF)
- 點陣圖
- 文字墨水
- 草圖墨水
文字筆跡和草圖筆跡是兩種類型的筆跡控件,分別做為文字或繪圖。 可以將ISF、文字筆跡和草圖筆跡貼到現有的筆跡中。
除了剪貼簿,此範例也會說明如何使用套索工具選取筆劃。 用戶可以移動選取的筆劃,並修改其繪圖屬性。 這項功能是筆跡重疊控件已提供之選取功能的子集;這裡實作是為了說明目的。
此範例會使用下列功能:
- InkCollector 物件。
- 筆跡剪貼簿支援。
- 使用套索搭配 Microsoft.Ink.Ink.HitTest 方法。
此範例示範轉譯筆跡、複製該筆跡,然後將筆跡貼到另一個應用程式,例如Microsoft Paint。
收集筆跡並設定表單
首先,參考與 Microsoft Windows<實體類型=“reg”/> XP Tablet PC Edition 軟體開發工具包 (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 方法決定。
剪下和複製命令都使用協助程式方法將筆跡複製到剪貼簿。 Cut 命令會使用協助程式方法來刪除選取的筆劃。
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 命令會將 NewPackets 和 Stroke 事件處理程式新增至筆跡收集器,Ink 命令會從筆跡收集器中移除這些事件處理程式。
複製筆劃時,剪貼簿上可用的格式會列在 [格式] 功能表上,而用戶會選取從此清單中複製筆跡的格式。 可用的格式類型包括筆跡串行化格式 (ISF)、元檔、增強型元檔及點陣圖。 草圖墨跡與文字墨跡格式是互相排斥的,並依賴於將墨跡作為 OLE 物件複製到剪貼簿中。
[樣式] 功能表可讓使用者變更畫筆的色彩和寬度屬性,以及任何選取的筆劃。
例如,Red 命令會將筆跡收集器 DefaultDrawingAttributes 屬性的 Color 屬性設定為紅色。 由於尚未設定 Cursor 物件的 DrawingAttributes 屬性,因此任何繪製至筆跡收集器的新筆跡都會繼承為預設繪圖色彩。 此外,如果目前選取了任何筆劃,則每個筆劃的「顏色」屬性也會被更新。
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 方法來取得套索點所選取的筆劃,並更新表單的選取狀態。 最後,觸發事件的筆劃從選取筆劃的集合中移除,套索點集合被清空,並且使用協助方法繪製選取矩形。
// 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 欄位,如果集合 NULL 或 EMPTY,則選取矩形會設為空矩形。 如果選取的 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。