Создание элемента управления рукописным вводом
Вы можете создать пользовательский элемент управления, который динамически и статически отображает цифровое чернило. То есть, отображение чернил во время рисования пользователем штриха, что создаёт эффект "течения" чернил из пера планшета, и отображение чернил после их добавления в элемент управления — будь то через перо планшета, вставка из буфера обмена или загрузка из файла. Для динамической отрисовки рукописного ввода элемент управления должен использовать DynamicRenderer. Для статической визуализации рукописного ввода необходимо переопределить методы обработки событий пера (OnStylusDown, OnStylusMoveи OnStylusUp), чтобы собирать данные StylusPoint, создавать штрихи и добавлять их в InkPresenter (который отображает рукописный ввод на элементе управления).
В этом разделе содержатся следующие подразделы:
Как: Собирать данные точек стилуса и создавать мазки чернила
Использование дополнительных подключаемых модулей и DynamicRenderers
Практическое руководство. Сбор данных точки пера и создание росчерков рукописного ввода
Чтобы создать элемент управления, который собирает и управляет рукописными штрихами, выполните следующие действия:
Создайте класс от 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; }
Присоедините RootVisual от DynamicRenderer к InkPresenter, вызвав метод AttachVisuals, и добавьте 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 и добавьте данные StylusPoint в созданный ранее объект StylusPointCollection.
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 и создайте новый Stroke с данными StylusPointCollection. Добавьте созданный вами новый элемент Stroke в коллекцию Strokes объекта InkPresenter и прекратите захват стилуса.
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, используя данные точки. Добавьте StylusPoint в созданный ранее объект StylusPointCollection.
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. Создайте новый Stroke с данными StylusPointCollection и добавьте созданный вами новый Stroke в коллекцию Strokes объекта InkPresenter.
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. Порядок объектов StylusPlugIn в StylusPlugInCollection влияет на отображение чернил при отрисовке. Пусть у вас есть DynamicRenderer с именем dynamicRenderer
и пользовательский StylusPlugIn под названием translatePlugin
, который корректирует отрыв чернил от пера планшета. Если translatePlugin
является первым StylusPlugIn в StylusPlugInCollection, а dynamicRenderer
— вторым, чернила, которые "текут", будут смещаться по мере того, как пользователь перемещает перо. Если dynamicRenderer
первый, и translatePlugin
второй, чернила не будут наноситься, пока пользователь не поднимет перо.
Заключение
Вы можете создать элемент управления, который собирает и отрисовывает рукописный ввод, переопределяя методы событий стилуса. Создавая собственный элемент управления, создавая собственные классы StylusPlugIn и вставляя их в StylusPlugInCollection, вы можете реализовать практически любое поведение с помощью цифрового рукописного ввода. У вас есть доступ к данным StylusPoint по мере их генерации, что даёт вам возможность настраивать вход Stylus и отображать его на экране соответственно вашим приложениям. Поскольку у вас есть низкоуровневый доступ к данным StylusPoint, вы можете собрать данные рукописного ввода и визуализировать их, обеспечив оптимальную производительность для вашего приложения.
См. также
.NET Desktop feedback