잉크 입력 컨트롤 만들기
잉크를 동적 및 정적으로 렌더링하는 사용자 지정 컨트롤을 만들 수 있습니다. 즉, 사용자가 스트로크를 그릴 때 잉크를 렌더링하여 잉크가 태블릿 펜에서 “흐르는” 것처럼 보이도록 하고 클립보드에서 붙여넣거나 파일에서 로드된 태블릿 펜을 통해 잉크가 컨트롤에 추가된 후 잉크를 표시합니다. 잉크를 동적으로 렌더링하려면 컨트롤에서 DynamicRenderer를 사용해야 합니다. 잉크를 정적으로 렌더링하려면 스타일러스 이벤트 메서드(OnStylusDown, OnStylusMove, OnStylusUp)를 재정의하여 StylusPoint 데이터를 수집하고, 스트로크를 만들고, 컨트롤에서 잉크를 렌더링하는 InkPresenter 메서드에 추가해야 합니다.
이 항목에는 다음과 같은 하위 단원이 포함되어 있습니다.
방법: 스타일러스 포인트 데이터 수집 및 잉크 스트로크 만들기
잉크 스트로크를 수집하고 관리하는 컨트롤을 만들려면 다음을 수행합니다.
Control 또는 Control에서 파생된 클래스(예: Label) 중 하나에서 클래스를 파생시킵니다.
using System; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Input.StylusPlugIns; using System.Windows.Controls; using System.Windows;
class InkControl : Label {
}
InkPresenter를 클래스에 추가하고 Content 속성을 새 InkPresenter로 설정합니다.
InkPresenter ip; public InkControl() { // Add an InkPresenter for drawing. ip = new InkPresenter(); this.Content = ip; }
AttachVisuals를 호출하여 DynamicRenderer의 RootVisual을 InkPresenter에 연결하고, DynamicRenderer를 StylusPlugIns 컬렉션에 추가합니다. 이렇게 하면 컨트롤이 스타일러스 포인트 데이터를 수집할 때 InkPresenter가 잉크를 표시할 수 있습니다.
public InkControl() {
// Add a dynamic renderer that // draws ink as it "flows" from the stylus. dr = new DynamicRenderer(); ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes); this.StylusPlugIns.Add(dr); }
OnStylusDown 메서드를 재정의합니다. 이 메서드에서 Capture 호출을 사용하여 스타일러스를 캡처합니다. 스타일러스를 캡처하면 스타일러스가 컨트롤의 경계를 벗어나더라도 컨트롤이 계속해서 StylusMove 및 StylusUp 이벤트를 수신합니다. 이는 반드시 필수는 아니지만 좋은 사용자 환경을 위해 거의 항상 필요합니다. 새 StylusPointCollection을 만들어 StylusPoint 데이터를 수집합니다. 마지막으로, StylusPoint의 초기 데이터를 StylusPointCollection에 추가합니다.
protected override void OnStylusDown(StylusDownEventArgs e) { // Capture the stylus so all stylus input is routed to this control. Stylus.Capture(this); // Allocate memory for the StylusPointsCollection and // add the StylusPoints that have come in so far. stylusPoints = new StylusPointCollection(); StylusPointCollection eventPoints = e.GetStylusPoints(this, stylusPoints.Description); stylusPoints.Add(eventPoints); }
OnStylusMove 메서드를 재정의하고 이전에 만든 StylusPointCollection 개체에 StylusPoint 데이터를 추가합니다.
protected override void OnStylusMove(StylusEventArgs e) { if (stylusPoints == null) { return; } // Add the StylusPoints that have come in since the // last call to OnStylusMove. StylusPointCollection newStylusPoints = e.GetStylusPoints(this, stylusPoints.Description); stylusPoints.Add(newStylusPoints); }
OnStylusUp 메서드를 재정의하고 StylusPointCollection 데이터를 사용하여 새 Stroke 메서드를 만듭니다. 직접 만든 새 Stroke를 InkPresenter의 Strokes 컬렉션에 추가하고 스타일러스 캡처를 해제합니다.
protected override void OnStylusUp(StylusEventArgs e) { if (stylusPoints == null) { return; } // Add the StylusPoints that have come in since the // last call to OnStylusMove. StylusPointCollection newStylusPoints = e.GetStylusPoints(this, stylusPoints.Description); stylusPoints.Add(newStylusPoints); // Create a new stroke from all the StylusPoints since OnStylusDown. Stroke stroke = new Stroke(stylusPoints); // Add the new stroke to the Strokes collection of the InkPresenter. ip.Strokes.Add(stroke); // Clear the StylusPointsCollection. stylusPoints = null; // Release stylus capture. Stylus.Capture(null); }
방법: 컨트롤이 마우스의 입력을 수락하도록 설정
이전 컨트롤을 애플리케이션에 추가하고, 실행하고, 입력 디바이스로 마우스를 사용하는 경우 스트로크가 유지되지 않는 것을 알 수 있습니다. 입력 디바이스로 마우스를 사용할 때 스트로크를 유지하려면 다음을 수행합니다.
OnMouseLeftButtonDown을 재정의하고 새 StylusPointCollection을 만듭니다. 이벤트가 발생했을 때 마우스의 위치를 가져오고, 포인트 데이터를 사용하여 StylusPoint를 만들고, StylusPoint를 StylusPointCollection에 추가합니다.
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); // If a stylus generated this event, return. if (e.StylusDevice != null) { return; } // Start collecting the points. stylusPoints = new StylusPointCollection(); Point pt = e.GetPosition(this); stylusPoints.Add(new StylusPoint(pt.X, pt.Y)); }
OnMouseMove 메서드를 재정의합니다. 이벤트가 발생했을 때 마우스의 위치를 가져오고 포인트 데이터를 사용하여 StylusPoint를 만듭니다. 이전에 만든 StylusPointCollection에 StylusPoint를 추가합니다.
protected override void OnMouseMove(MouseEventArgs e) { base.OnMouseMove(e); // If a stylus generated this event, return. if (e.StylusDevice != null) { return; } // Don't collect points unless the left mouse button // is down. if (e.LeftButton == MouseButtonState.Released || stylusPoints == null) { return; } Point pt = e.GetPosition(this); stylusPoints.Add(new StylusPoint(pt.X, pt.Y)); }
OnMouseLeftButtonUp 메서드를 재정의합니다. StylusPointCollection 데이터를 사용하여 새 Stroke를 만들고 직접 만든 새 Stroke를 InkPresenter의 Strokes 컬렉션에 추가합니다.
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); // If a stylus generated this event, return. if (e.StylusDevice != null) { return; } if (stylusPoints == null) { return; } Point pt = e.GetPosition(this); stylusPoints.Add(new StylusPoint(pt.X, pt.Y)); // Create a stroke and add it to the InkPresenter. Stroke stroke = new Stroke(stylusPoints); stroke.DrawingAttributes = dr.DrawingAttributes; ip.Strokes.Add(stroke); stylusPoints = null; }
통합
다음 예제는 사용자가 마우스 또는 펜을 사용할 때 잉크를 수집하는 사용자 지정 컨트롤입니다.
using System;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Controls;
using System.Windows;
// A control for managing ink input
class InkControl : Label
{
InkPresenter ip;
DynamicRenderer dr;
// The StylusPointsCollection that gathers points
// before Stroke from is created.
StylusPointCollection stylusPoints = null;
public InkControl()
{
// Add an InkPresenter for drawing.
ip = new InkPresenter();
this.Content = ip;
// Add a dynamic renderer that
// draws ink as it "flows" from the stylus.
dr = new DynamicRenderer();
ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes);
this.StylusPlugIns.Add(dr);
}
static InkControl()
{
// Allow ink to be drawn only within the bounds of the control.
Type owner = typeof(InkControl);
ClipToBoundsProperty.OverrideMetadata(owner,
new FrameworkPropertyMetadata(true));
}
protected override void OnStylusDown(StylusDownEventArgs e)
{
// Capture the stylus so all stylus input is routed to this control.
Stylus.Capture(this);
// Allocate memory for the StylusPointsCollection and
// add the StylusPoints that have come in so far.
stylusPoints = new StylusPointCollection();
StylusPointCollection eventPoints =
e.GetStylusPoints(this, stylusPoints.Description);
stylusPoints.Add(eventPoints);
}
protected override void OnStylusMove(StylusEventArgs e)
{
if (stylusPoints == null)
{
return;
}
// Add the StylusPoints that have come in since the
// last call to OnStylusMove.
StylusPointCollection newStylusPoints =
e.GetStylusPoints(this, stylusPoints.Description);
stylusPoints.Add(newStylusPoints);
}
protected override void OnStylusUp(StylusEventArgs e)
{
if (stylusPoints == null)
{
return;
}
// Add the StylusPoints that have come in since the
// last call to OnStylusMove.
StylusPointCollection newStylusPoints =
e.GetStylusPoints(this, stylusPoints.Description);
stylusPoints.Add(newStylusPoints);
// Create a new stroke from all the StylusPoints since OnStylusDown.
Stroke stroke = new Stroke(stylusPoints);
// Add the new stroke to the Strokes collection of the InkPresenter.
ip.Strokes.Add(stroke);
// Clear the StylusPointsCollection.
stylusPoints = null;
// Release stylus capture.
Stylus.Capture(null);
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
// If a stylus generated this event, return.
if (e.StylusDevice != null)
{
return;
}
// Start collecting the points.
stylusPoints = new StylusPointCollection();
Point pt = e.GetPosition(this);
stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// If a stylus generated this event, return.
if (e.StylusDevice != null)
{
return;
}
// Don't collect points unless the left mouse button
// is down.
if (e.LeftButton == MouseButtonState.Released ||
stylusPoints == null)
{
return;
}
Point pt = e.GetPosition(this);
stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
// If a stylus generated this event, return.
if (e.StylusDevice != null)
{
return;
}
if (stylusPoints == null)
{
return;
}
Point pt = e.GetPosition(this);
stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
// Create a stroke and add it to the InkPresenter.
Stroke stroke = new Stroke(stylusPoints);
stroke.DrawingAttributes = dr.DrawingAttributes;
ip.Strokes.Add(stroke);
stylusPoints = null;
}
}
추가 플러그 인 및 DynamicRenderer 사용
InkCanvas와 마찬가지로 사용자 지정 컨트롤에는 사용자 지정 StylusPlugIn 및 추가 DynamicRenderer 개체가 있을 수 있습니다. StylusPlugIns 컬렉션에 이 개체를 추가합니다. 순서는 StylusPlugIn 개체의 StylusPlugInCollection 렌더링 될 때 적용 될 잉크의 모양을. 가정은 DynamicRenderer 라는 dynamicRenderer
및 사용자 지정 StylusPlugIn 라는 translatePlugin
태블릿 펜에서 잉크를 오프셋 하는. 경우 translatePlugin
첫 번째 StylusPlugIn 에 StylusPlugInCollection, 및 dynamicRenderer
, 두 번째는 사용자가 펜을 움직이면 "흐르는" 잉크가 오프셋 됩니다. dynamicRenderer
가 첫 번째이고 translatePlugin
이 두 번째인 경우 사용자가 펜을 들어올릴 때까지 잉크가 오프셋되지 않습니다.
결론
스타일러스 이벤트 메서드를 재정의하여 잉크를 수집하고 렌더링하는 컨트롤을 만들 수 있습니다. 고유한 컨트롤을 만들고, 고유한 StylusPlugIn 클래스를 파생시키고, StylusPlugInCollection에 삽입하면 디지털 잉크로 상상할 수 있는 거의 모든 동작을 구현할 수 있습니다. StylusPoint 데이터가 생성될 때 이 데이터에 액세스할 수 있으므로 Stylus 입력을 사용자 지정하고 애플리케이션에 맞게 화면에서 렌더링할 수 있습니다. StylusPoint 데이터에 대한 액세스 수준이 낮기 때문에 잉크 컬렉션을 구현하고 애플리케이션에 대한 최적 성능으로 렌더링할 수 있습니다.
참고 항목
.NET Desktop feedback