Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La propiedad DrawingAttributes de un trazo permite especificar su apariencia (por ejemplo, su tamaño, color y forma), pero puede haber ocasiones en las que desee personalizar la apariencia más de lo que DrawingAttributes permite. Quizás desee personalizar la apariencia de la entrada de lápiz representándola con la apariencia de un aerógrafo, pintura al óleo y muchos otros efectos. Windows Presentation Foundation (WPF) permite personalizar la representación de la entrada de lápiz mediante la implementación de un DynamicRenderer personalizado y un objeto Stroke.
Este tema contiene las siguientes subsecciones:
Arquitectura
Implementación de un representador dinámico
Implementing a Custom Stroke
Implementación de una clase InkCanvas personalizada
Conclusión
Arquitectura
La representación de la entrada de lápiz se produce en dos tiempos: cuando el usuario escribe en una superficie habilitada para entrada de lápiz y después de que se agrega el trazo a dicha superficie. DynamicRenderer representa la entrada de lápiz cuando el usuario mueve el lápiz de Tablet PC en el digitalizador y Stroke se representa después de que se ha agregado a un elemento.
Al representar la entrada de lápiz de forma dinámica, se implementan tres clases.
DynamicRenderer: implemente una clase que se derive de DynamicRenderer. Esta clase es un StylusPlugIn especializado que representa el trazo a medida que se dibuja. DynamicRenderer realiza la representación en un subproceso aparte, por lo que la superficie de entrada de lápiz parece recopilar la entrada de lápiz incluso cuando el subproceso de la interfaz de usuario de la aplicación está bloqueado. Para obtener más información acerca del modelo de subprocesos, consulte Modelo de subprocesamiento de entrada manuscrita. Para personalizar la representación dinámica de un trazo, invalide el método OnDraw.
Stroke: implemente una clase que se derive de Stroke. Esta clase es responsable de la representación estática de los datos de StylusPoint una vez convertidos en un objeto Stroke. Invalide el método DrawCore para asegurarse de que la representación estática del trazo es coherente con la representación dinámica.
InkCanvas: implemente una clase que se derive de InkCanvas. Asigne el objeto DynamicRenderer personalizado a la propiedad DynamicRenderer. Invalide el método OnStrokeCollected y agregue un trazo personalizado a la propiedad Strokes. De esta forma se garantiza que la apariencia de la entrada de lápiz es coherente.
Implementación de un representador dinámico
Aunque la clase DynamicRenderer es una parte estándar de WPF, para realizar una representación más especializada, debe crear un representador dinámico personalizado que se derive de DynamicRenderer e invalidar el método OnDraw.
En el ejemplo siguiente se muestra un DynamicRenderer personalizado que dibuja la entrada de lápiz con un efecto de pincel de degradado lineal.
Imports System
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
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 'OnStylusDown
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 'OnDraw
End Class 'CustomDynamicRenderer
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using 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.
if (brush == null)
{
brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
}
// Create a new Pen, if necessary.
if (pen == null)
{
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;
}
}
}
}
Implementación de trazos personalizados
Implemente una clase que se derive de Stroke. Esta clase es responsable de la representación de los datos de StylusPoint una vez convertidos en un objeto Stroke. Invalide la clase DrawCore para crear el verdadero dibujo.
Su clase Stroke también puede almacenar datos personalizados si usa el método AddPropertyData. Estos datos se almacenan con los datos de trazo cuando se guardan.
La clase Stroke también puede realizar pruebas de posicionamiento. También puede implementar un algoritmo de prueba de posicionamiento propio si invalida el método HitTest en la clase actual.
El código de C# siguiente muestra una clase Stroke personalizada que representa los datos de StylusPoint como un trazo 3D.
Imports System
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
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 'New
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 'DrawCore
End Class 'CustomStroke
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using 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;
}
}
}
}
Implementación de una clase InkCanvas personalizada
La manera más fácil de usar su DynamicRenderer y trazo personalizados es implementar una clase que se derive de InkCanvas y use estas clases. InkCanvas tiene una propiedad DynamicRenderer que especifica cómo se representa el trazo cuando el usuario está dibujándolo.
Para personalizar la representación de trazos en InkCanvas, realice estos pasos:
Cree una clase que se derive de InkCanvas.
Asigne su DynamicRenderer personalizado a la propiedad InkCanvas.DynamicRenderer.
Invalide el método OnStrokeCollected. En este método, quite el trazo original que se agregó a InkCanvas. A continuación, cree un trazo personalizado, agréguelo a la propiedad Strokes y llame a la clase base con una nueva clase InkCanvasStrokeCollectedEventArgs que contenga el trazo personalizado.
El código de C# siguiente muestra una clase InkCanvas personalizada que usa un DynamicRenderer personalizado y recopila los trazos personalizados.
Public Class CustomRenderingInkCanvas
Inherits InkCanvas
Private customRenderer As New CustomDynamicRenderer()
Public Sub New()
' Use the custom dynamic renderer on the
' custom InkCanvas.
Me.DynamicRenderer = customRenderer
End Sub 'New
Protected Overrides Sub OnStrokeCollected(ByVal e As InkCanvasStrokeCollectedEventArgs)
' Remove the original stroke and add a custom stroke.
Me.Strokes.Remove(e.Stroke)
Dim customStroke As New CustomStroke(e.Stroke.StylusPoints)
Me.Strokes.Add(customStroke)
' Pass the custom stroke to base class' OnStrokeCollected method.
Dim args As New InkCanvasStrokeCollectedEventArgs(customStroke)
MyBase.OnStrokeCollected(args)
End Sub 'OnStrokeCollected
End Class 'CustomRenderingInkCanvas
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 puede tener más de un DynamicRenderer. Para agregar varios objetos DynamicRenderer a InkCanvas, agréguelos a la propiedad StylusPlugIns.
Conclusión
Puede personalizar la apariencia de la entrada de lápiz al derivar sus propias clases DynamicRenderer, Stroke y InkCanvas. Juntas, estas clases garantizan que la apariencia del trazo es coherente cuando el usuario dibuja el trazo y después de que se ha recopilado.