Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Свойство DrawingAttributes штриха позволяет указать внешний вид штриха, например его размер, цвет и форму, но может потребоваться настроить внешний вид за пределами того, что DrawingAttributes разрешает. Вы можете настроить вид чернил, добавляя эффекты аэрографа, масляной краски и многие другие. Windows Presentation Foundation (WPF) позволяет выполнять настраиваемое отображение рукописного ввода путем реализации пользовательских объектов DynamicRenderer и Stroke.
В этом разделе содержатся следующие подразделы:
Архитектура
Визуализация рукописного ввода происходит два раза: когда пользователь пишет на поверхность рукописного ввода и снова после добавления штриха в область, поддерживающую рукописный ввод. DynamicRenderer рисует чернила, когда пользователь перемещает перо планшета по дигитайзеру, а Stroke отображается, как только добавляется в элемент.
Существует три класса для использования при динамической отрисовке рукописного ввода.
DynamicRenderer: реализуйте класс, производный от DynamicRenderer. Этот класс является специализированным StylusPlugIn, который рендерит штрих в процессе рисования. DynamicRenderer выполняет отрисовку в отдельном потоке, поэтому поверхность ввода кажется продолжающей собирать чернила, даже когда блокируется поток пользовательского интерфейса приложения. Дополнительные сведения о модели потоков см. в модели потоков рукописного ввода. Чтобы настроить динамически отрисовку штриха, переопределите метод OnDraw.
Stroke: реализуйте класс, который наследуется от Stroke. Этот класс отвечает за статическую отрисовку данных StylusPoint после преобразования в объект Stroke. Переопределите метод DrawCore, чтобы обеспечить согласованность статической отрисовки штриха с динамической отрисовкой.
InkCanvas: Реализуйте класс, производный от InkCanvas. Назначьте настраиваемый DynamicRenderer свойству DynamicRenderer. Переопределите метод OnStrokeCollected и добавьте пользовательский штрих к свойству Strokes. Это гарантирует однородность внешнего вида чернил.
Реализация динамического отрисовщика
Хотя класс DynamicRenderer является стандартной частью WPF, для выполнения более специализированной отрисовки необходимо создать настраиваемый динамический отрисовщик, производный от DynamicRenderer, и переопределить метод OnDraw.
В следующем примере показан настроенный элемент DynamicRenderer, который рисует с эффектом линейного градиентного кисти.
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using System.Windows.Ink;
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Input
Imports System.Windows.Ink
// A StylusPlugin that renders ink with a linear gradient brush effect.
class CustomDynamicRenderer : DynamicRenderer
{
[ThreadStatic]
static private Brush brush = null;
[ThreadStatic]
static private Pen pen = null;
private Point prevPoint;
protected override void OnStylusDown(RawStylusInput rawStylusInput)
{
// Allocate memory to store the previous point to draw from.
prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
base.OnStylusDown(rawStylusInput);
}
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush)
{
// Create a new Brush, if necessary.
brush ??= new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
// Create a new Pen, if necessary.
pen ??= new Pen(brush, 2d);
// Draw linear gradient ellipses between
// all the StylusPoints that have come in.
for (int i = 0; i < stylusPoints.Count; i++)
{
Point pt = (Point)stylusPoints[i];
Vector v = Point.Subtract(prevPoint, pt);
// Only draw if we are at least 4 units away
// from the end of the last ellipse. Otherwise,
// we're just redrawing and wasting cycles.
if (v.Length > 4)
{
// Set the thickness of the stroke based
// on how hard the user pressed.
double radius = stylusPoints[i].PressureFactor * 10d;
drawingContext.DrawEllipse(brush, pen, pt, radius, radius);
prevPoint = pt;
}
}
}
}
' A StylusPlugin that renders ink with a linear gradient brush effect.
Class CustomDynamicRenderer
Inherits DynamicRenderer
<ThreadStatic()> _
Private Shared brush As Brush = Nothing
<ThreadStatic()> _
Private Shared pen As Pen = Nothing
Private prevPoint As Point
Protected Overrides Sub OnStylusDown(ByVal rawStylusInput As RawStylusInput)
' Allocate memory to store the previous point to draw from.
prevPoint = New Point(Double.NegativeInfinity, Double.NegativeInfinity)
MyBase.OnStylusDown(rawStylusInput)
End Sub
Protected Overrides Sub OnDraw(ByVal drawingContext As DrawingContext, _
ByVal stylusPoints As StylusPointCollection, _
ByVal geometry As Geometry, _
ByVal fillBrush As Brush)
' Create a new Brush, if necessary.
If brush Is Nothing Then
brush = New LinearGradientBrush(Colors.Red, Colors.Blue, 20.0)
End If
' Create a new Pen, if necessary.
If pen Is Nothing Then
pen = New Pen(brush, 2.0)
End If
' Draw linear gradient ellipses between
' all the StylusPoints that have come in.
Dim i As Integer
For i = 0 To stylusPoints.Count - 1
Dim pt As Point = CType(stylusPoints(i), Point)
Dim v As Vector = Point.Subtract(prevPoint, pt)
' Only draw if we are at least 4 units away
' from the end of the last ellipse. Otherwise,
' we're just redrawing and wasting cycles.
If v.Length > 4 Then
' Set the thickness of the stroke based
' on how hard the user pressed.
Dim radius As Double = stylusPoints(i).PressureFactor * 10.0
drawingContext.DrawEllipse(brush, pen, pt, radius, radius)
prevPoint = pt
End If
Next i
End Sub
End Class
Реализация пользовательских штрихов
Реализуйте класс, производный от Stroke. Этот класс отвечает за отрисовку StylusPoint данных после его преобразования в объект Stroke. Переопределите класс DrawCore для осуществления отрисовки.
Ваш класс Stroke также может хранить пользовательские данные, используя метод AddPropertyData. Эти данные хранятся с данными о штрихах при сохранении.
Класс Stroke также может выполнять тестирование попаданий. Вы также можете реализовать собственный алгоритм проверки попаданий, переопределив метод HitTest в текущем классе.
В следующем коде C# демонстрируется настраиваемый класс Stroke, который используется для отображения данных StylusPoint в виде трехмерного штриха.
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using System.Windows.Ink;
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Input
Imports System.Windows.Ink
// A class for rendering custom strokes
class CustomStroke : Stroke
{
Brush brush;
Pen pen;
public CustomStroke(StylusPointCollection stylusPoints)
: base(stylusPoints)
{
// Create the Brush and Pen used for drawing.
brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
pen = new Pen(brush, 2d);
}
protected override void DrawCore(DrawingContext drawingContext,
DrawingAttributes drawingAttributes)
{
// Allocate memory to store the previous point to draw from.
Point prevPoint = new Point(double.NegativeInfinity,
double.NegativeInfinity);
// Draw linear gradient ellipses between
// all the StylusPoints in the Stroke.
for (int i = 0; i < this.StylusPoints.Count; i++)
{
Point pt = (Point)this.StylusPoints[i];
Vector v = Point.Subtract(prevPoint, pt);
// Only draw if we are at least 4 units away
// from the end of the last ellipse. Otherwise,
// we're just redrawing and wasting cycles.
if (v.Length > 4)
{
// Set the thickness of the stroke
// based on how hard the user pressed.
double radius = this.StylusPoints[i].PressureFactor * 10d;
drawingContext.DrawEllipse(brush, pen, pt, radius, radius);
prevPoint = pt;
}
}
}
}
' A class for rendering custom strokes
Class CustomStroke
Inherits Stroke
Private brush As Brush
Private pen As Pen
Public Sub New(ByVal stylusPoints As StylusPointCollection)
MyBase.New(stylusPoints)
' Create the Brush and Pen used for drawing.
brush = New LinearGradientBrush(Colors.Red, Colors.Blue, 20.0)
pen = New Pen(brush, 2.0)
End Sub
Protected Overrides Sub DrawCore(ByVal drawingContext As DrawingContext, _
ByVal drawingAttributes As DrawingAttributes)
' Allocate memory to store the previous point to draw from.
Dim prevPoint As New Point(Double.NegativeInfinity, Double.NegativeInfinity)
' Draw linear gradient ellipses between
' all the StylusPoints in the Stroke.
Dim i As Integer
For i = 0 To Me.StylusPoints.Count - 1
Dim pt As Point = CType(Me.StylusPoints(i), Point)
Dim v As Vector = Point.Subtract(prevPoint, pt)
' Only draw if we are at least 4 units away
' from the end of the last ellipse. Otherwise,
' we're just redrawing and wasting cycles.
If v.Length > 4 Then
' Set the thickness of the stroke
' based on how hard the user pressed.
Dim radius As Double = Me.StylusPoints(i).PressureFactor * 10.0
drawingContext.DrawEllipse(brush, pen, pt, radius, radius)
prevPoint = pt
End If
Next i
End Sub
End Class
Создание пользовательского InkCanvas
Самый простой способ использовать настраиваемые DynamicRenderer и обводку — это реализовать класс, который наследуется от InkCanvas и использует эти классы. InkCanvas имеет свойство DynamicRenderer, указывающее, как отрисовывается штрих при рисовании пользователем.
Чтобы настроить отрисовку линий на InkCanvas, выполните следующие действия:
Создайте класс, производный от InkCanvas.
Назначьте свой DynamicRenderer свойству InkCanvas.DynamicRenderer.
Переопределите метод OnStrokeCollected. В этом методе удалите исходный штрих, добавленный в InkCanvas. Затем создайте настраиваемый штрих, добавьте его в свойство Strokes и вызовите базовый класс с новым InkCanvasStrokeCollectedEventArgs, который содержит настраиваемый штрих.
В следующем коде C# демонстрируется пользовательский класс InkCanvas, использующий настраиваемый DynamicRenderer и собирающий пользовательские штрихи.
public class CustomRenderingInkCanvas : InkCanvas
{
CustomDynamicRenderer customRenderer = new CustomDynamicRenderer();
public CustomRenderingInkCanvas() : base()
{
// Use the custom dynamic renderer on the
// custom InkCanvas.
this.DynamicRenderer = customRenderer;
}
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
// Remove the original stroke and add a custom stroke.
this.Strokes.Remove(e.Stroke);
CustomStroke customStroke = new CustomStroke(e.Stroke.StylusPoints);
this.Strokes.Add(customStroke);
// Pass the custom stroke to base class' OnStrokeCollected method.
InkCanvasStrokeCollectedEventArgs args =
new InkCanvasStrokeCollectedEventArgs(customStroke);
base.OnStrokeCollected(args);
}
}
InkCanvas может иметь несколько DynamicRenderer. Вы можете добавить несколько объектов DynamicRenderer в InkCanvas, добавив их в свойство StylusPlugIns.
Заключение
Вы можете настроить внешний вид чернил, создав собственные классы DynamicRenderer, Strokeи InkCanvas. Вместе эти классы гарантируют, что внешний вид штриха остаётся единообразным, когда пользователь рисует штрих и после его обработки.
См. также
.NET Desktop feedback