インク入力コントロールの作成
インクを動的および静的にレンダリングするカスタム コントロールを作成できます。 つまり、ユーザーがストロークを描画するのに合わせてインクをレンダリングすることで、インクがタブレット ペンから "流れ出している" ように見えます。また、タブレット ペンの使用、クリップボードからの貼り付け、またはファイルからの読み込みにより、インクがコントロールに追加された後で、インクを表示します。 インクを動的にレンダリングするには、コントロールで 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;
}
}
追加のプラグインと DynamicRenderers を使用する
InkCanvas と同様に、カスタム コントロールでは、カスタム StylusPlugIn オブジェクトと追加の DynamicRenderer オブジェクトを使用できます。 これらを StylusPlugIns コレクションに追加します。 StylusPlugInCollection 内の StylusPlugIn オブジェクトの順序は、レンダリング時のインクの外観に影響します。 dynamicRenderer
という名前の DynamicRenderer と、タブレット ペンからのインクをオフセットする translatePlugin
という名前のカスタム StylusPlugIn があるとします。 translatePlugin
が StylusPlugInCollection 内の最初の StylusPlugIn であり、dynamicRenderer
が 2 番目の場合は、ユーザーがペンを動かすと、"流れ出す" インクはオフセットされます。 dynamicRenderer
が最初で、translatePlugin
が 2 番目の場合は、ユーザーがペンを持ち上げるまでインクはオフセットされません。
まとめ
スタイラス イベント メソッドをオーバーライドすることにより、インクを収集してレンダリングするコントロールを作成できます。 独自のコントロールを作成し、独自の StylusPlugIn クラスを派生させ、それを StylusPlugInCollection に挿入することにより、デジタル インクで想像できるほとんどどのような動作でも実装できます。 生成された StylusPoint データにアクセスできるため、Stylus の入力をカスタマイズし、アプリケーションに適した方法で画面にレンダリングする機会が得られます。 そのように低いレベルで StylusPoint のデータにアクセスできるため、アプリケーションに最適なパフォーマンスでインクのコレクションとレンダリングを実装できます。
関連項目
.NET Desktop feedback