インク入力コントロールの作成

インクを動的および静的にレンダリングするカスタム コントロールを作成できます。 つまり、ユーザーがストロークを描画するのに合わせてインクをレンダリングすることで、インクがタブレット ペンから "流れ出している" ように見えます。また、タブレット ペンの使用、クリップボードからの貼り付け、またはファイルからの読み込みにより、インクがコントロールに追加された後で、インクを表示します。 インクを動的にレンダリングするには、コントロールで DynamicRenderer を使用する必要があります。 インクを静的にレンダリングするには、スタイラス イベント メソッド (OnStylusDownOnStylusMoveOnStylusUp) をオーバーライドして、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 を呼び出してスタイラスをキャプチャします。 スタイラスをキャプチャすることにより、スタイラスがコントロールの境界から出た場合でも、コントロールは 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);
    }
    
  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 を作成します。 前に作成した 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));
    }
    
  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;
    }
}

追加のプラグインと DynamicRenderers を使用する

InkCanvas と同様に、カスタム コントロールでは、カスタム StylusPlugIn オブジェクトと追加の DynamicRenderer オブジェクトを使用できます。 これらを StylusPlugIns コレクションに追加します。 StylusPlugInCollection 内の StylusPlugIn オブジェクトの順序は、レンダリング時のインクの外観に影響します。 dynamicRenderer という名前の DynamicRenderer と、タブレット ペンからのインクをオフセットする translatePlugin という名前のカスタム StylusPlugIn があるとします。 translatePluginStylusPlugInCollection 内の最初の StylusPlugIn であり、dynamicRenderer が 2 番目の場合は、ユーザーがペンを動かすと、"流れ出す" インクはオフセットされます。 dynamicRenderer が最初で、translatePlugin が 2 番目の場合は、ユーザーがペンを持ち上げるまでインクはオフセットされません。

まとめ

スタイラス イベント メソッドをオーバーライドすることにより、インクを収集してレンダリングするコントロールを作成できます。 独自のコントロールを作成し、独自の StylusPlugIn クラスを派生させ、それを StylusPlugInCollection に挿入することにより、デジタル インクで想像できるほとんどどのような動作でも実装できます。 生成された StylusPoint データにアクセスできるため、Stylus の入力をカスタマイズし、アプリケーションに適した方法で画面にレンダリングする機会が得られます。 そのように低いレベルで StylusPoint のデータにアクセスできるため、アプリケーションに最適なパフォーマンスでインクのコレクションとレンダリングを実装できます。

関連項目