이 프로그램은 잉크를 복사하여 다른 애플리케이션에 붙여넣는 방법을 보여 줍니다. 또한 사용자가 선택한 스트로크를 복사하고 결과를 기존 잉크 개체에 붙여넣을 수 있습니다.
사용할 수 있는 클립보드 모드는 다음과 같습니다.
- 잉크 직렬 형식(ISF)
- 메타파일
- EMF(고급 메타파일)
- 비트맵
- 텍스트 잉크
- 스케치용 잉크
텍스트 잉크와 스케치 잉크는 각각 텍스트 또는 그리기로 사용되는 두 가지 유형의 잉크 컨트롤입니다. ISF, 텍스트 잉크 및 스케치 잉크를 기존 잉크에 붙여넣을 수 있습니다.
클립보드 외에도 이 샘플에서는 올가미 도구를 사용하여 스트로크를 선택하는 방법을 보여 줍니다. 사용자는 선택한 스트로크를 이동하고 그리기 특성을 수정할 수 있습니다. 이 기능은 잉크 오버레이 컨트롤에서 이미 제공하는 선택 기능의 하위 집합입니다. 설명용으로 구현됩니다.
이 샘플에서는 다음 기능이 사용됩니다.
- InkCollector 개체입니다.
- 잉크 클립보드 지원.
- Microsoft.Ink.Ink.HitTest 메서드와 함께 올가미를 사용합니다.
이 샘플에서는 잉크를 렌더링하고, 잉크를 복사한 다음, 잉크를 Microsoft Paint와 같은 다른 애플리케이션에 붙여넣는 방법을 보여 줍니다.
잉크 수집 및 양식 설정
먼저 Microsoft Windows<엔터티 type="reg"/> XP Tablet PC Edition SDK(소프트웨어 개발 키트)와 함께 설치된 태블릿 PC 자동화 인터페이스를 참조합니다.
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 메서드를 사용하여 결정되는 클립보드 내용에 따라 붙여넣기 명령을 사용하도록 설정합니다.
잘라내기 및 복사 명령은 모두 도우미 메서드를 사용하여 잉크를 클립보드에 복사합니다. 잘라내기 명령은 도우미 메서드를 사용하여 선택한 스트로크를 삭제합니다.
붙여넣기 명령은 먼저 Ink 개체의 CanPaste 메서드를 검사하여 클립보드의 개체를 붙여넣을 수 있는지 확인합니다. 그런 다음 붙여넣기 명령은 붙여넣기 영역의 왼쪽 위 모서리를 계산하고, 좌표를 픽셀에서 잉크 공간으로 변환하고, 클립보드에서 잉크 수집기로 스트로크를 붙여넣습니다. 마지막으로 선택 상자가 업데이트됩니다.
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 이벤트 처리기를 잉크 수집기에 추가하고 잉크 명령은 잉크 수집기에서 이러한 이벤트 처리기를 제거합니다.
스트로크가 복사될 때 클립보드에서 사용할 수 있는 형식이 서식 메뉴에 나열되고 사용자는 이 목록에서 잉크를 복사하기 위한 형식을 선택합니다. 사용할 수 있는 형식에는 ISF(Ink Serialized Format), 메타파일, 향상된 메타파일 및 비트맵이 포함됩니다. 스케치 잉크와 텍스트 잉크 형식은 함께 사용할 수 없으며 클립보드에 복사되는 잉크를 OLE 개체로 사용합니다.
스타일 메뉴를 사용하면 펜의 색 및 너비 속성과 선택한 스트로크를 변경할 수 있습니다.
예를 들어 빨간색 명령은 잉크 수집기의 DefaultDrawingAttributes 속성의 Color 속성을 빨간색으로 설정합니다. 커서 개체의 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 도우미 메서드는 선택한 Strokes 필드를 업데이트하며, 컬렉션이 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를 삭제합니다.