잉크 입력 컨트롤 만들기

잉크를 동적 및 정적으로 렌더링하는 사용자 지정 컨트롤을 만들 수 있습니다. 즉, 사용자가 스트로크를 그릴 때 잉크를 렌더링하여 잉크가 태블릿 펜에서 “흐르는” 것처럼 보이도록 하고 클립보드에서 붙여넣거나 파일에서 로드된 태블릿 펜을 통해 잉크가 컨트롤에 추가된 후 잉크를 표시합니다. 잉크를 동적으로 렌더링하려면 컨트롤에서 DynamicRenderer를 사용해야 합니다. 잉크를 정적으로 렌더링하려면 스타일러스 이벤트 메서드(OnStylusDown, OnStylusMove, OnStylusUp)를 재정의하여 StylusPoint 데이터를 수집하고, 스트로크를 만들고, 컨트롤에서 잉크를 렌더링하는 InkPresenter 메서드에 추가해야 합니다.

이 항목에는 다음과 같은 하위 단원이 포함되어 있습니다.

방법: 스타일러스 포인트 데이터 수집 및 잉크 스트로크 만들기

잉크 스트로크를 수집하고 관리하는 컨트롤을 만들려면 다음을 수행합니다.

  1. 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
    {
    
    }
    
  2. InkPresenter를 클래스에 추가하고 Content 속성을 새 InkPresenter로 설정합니다.

    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. AttachVisuals를 호출하여 DynamicRendererRootVisualInkPresenter에 연결하고, DynamicRendererStylusPlugIns 컬렉션에 추가합니다. 이렇게 하면 컨트롤이 스타일러스 포인트 데이터를 수집할 때 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);
    }
    
  4. OnStylusDown 메서드를 재정의합니다. 이 메서드에서 Capture 호출을 사용하여 스타일러스를 캡처합니다. 스타일러스를 캡처하면 스타일러스가 컨트롤의 경계를 벗어나더라도 컨트롤이 계속해서 StylusMoveStylusUp 이벤트를 수신합니다. 이는 반드시 필수는 아니지만 좋은 사용자 환경을 위해 거의 항상 필요합니다. 새 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);
    }
    
  5. 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);
    }
    
  6. OnStylusUp 메서드를 재정의하고 StylusPointCollection 데이터를 사용하여 새 Stroke 메서드를 만듭니다. 직접 만든 새 StrokeInkPresenterStrokes 컬렉션에 추가하고 스타일러스 캡처를 해제합니다.

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

방법: 컨트롤이 마우스의 입력을 수락하도록 설정

이전 컨트롤을 애플리케이션에 추가하고, 실행하고, 입력 디바이스로 마우스를 사용하는 경우 스트로크가 유지되지 않는 것을 알 수 있습니다. 입력 디바이스로 마우스를 사용할 때 스트로크를 유지하려면 다음을 수행합니다.

  1. OnMouseLeftButtonDown을 재정의하고 새 StylusPointCollection을 만듭니다. 이벤트가 발생했을 때 마우스의 위치를 가져오고, 포인트 데이터를 사용하여 StylusPoint를 만들고, StylusPointStylusPointCollection에 추가합니다.

    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));
    }
    
  2. OnMouseMove 메서드를 재정의합니다. 이벤트가 발생했을 때 마우스의 위치를 가져오고 포인트 데이터를 사용하여 StylusPoint를 만듭니다. 이전에 만든 StylusPointCollectionStylusPoint를 추가합니다.

    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));
    }
    
  3. OnMouseLeftButtonUp 메서드를 재정의합니다. StylusPointCollection 데이터를 사용하여 새 Stroke를 만들고 직접 만든 새 StrokeInkPresenterStrokes 컬렉션에 추가합니다.

    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 첫 번째 StylusPlugInStylusPlugInCollection, 및 dynamicRenderer , 두 번째는 사용자가 펜을 움직이면 "흐르는" 잉크가 오프셋 됩니다. dynamicRenderer가 첫 번째이고 translatePlugin이 두 번째인 경우 사용자가 펜을 들어올릴 때까지 잉크가 오프셋되지 않습니다.

결론

스타일러스 이벤트 메서드를 재정의하여 잉크를 수집하고 렌더링하는 컨트롤을 만들 수 있습니다. 고유한 컨트롤을 만들고, 고유한 StylusPlugIn 클래스를 파생시키고, StylusPlugInCollection에 삽입하면 디지털 잉크로 상상할 수 있는 거의 모든 동작을 구현할 수 있습니다. StylusPoint 데이터가 생성될 때 이 데이터에 액세스할 수 있으므로 Stylus 입력을 사용자 지정하고 애플리케이션에 맞게 화면에서 렌더링할 수 있습니다. StylusPoint 데이터에 대한 액세스 수준이 낮기 때문에 잉크 컬렉션을 구현하고 애플리케이션에 대한 최적 성능으로 렌더링할 수 있습니다.

참고 항목