Encre de rendu personnalisé
La DrawingAttributes propriété d’un trait vous permet de spécifier l’apparence d’un trait, tel que sa taille, sa couleur et sa forme, mais il peut arriver que vous souhaitiez personnaliser l’apparence au-delà de ce qui DrawingAttributes lui est autorisé. Vous souhaiterez peut-être personnaliser l’apparence de l’encre en appliquant des effets tels qu’aérographe, peinture à l’huile et autres. Windows Presentation Foundation (WPF) vous permet d’effectuer un rendu manuscrit personnalisé en implémentant un objet et Stroke un personnaliséDynamicRenderer.
Cette rubrique contient les sous-sections suivantes :
Architecture
Le rendu de l’encre se produit deux fois : quand un utilisateur écrit de l’encre sur une surface d’entrée manuscrite, et une fois que le trait a été ajouté à la surface d’entrée manuscrite. Le DynamicRenderer rendu de l’encre lorsque l’utilisateur déplace le stylet de tablette sur le numériseur, et le Stroke rendu lui-même une fois qu’il est ajouté à un élément.
Vous devez implémenter trois classes lors du rendu dynamique de l’encre.
DynamicRenderer : implémentez une classe qui dérive de DynamicRenderer. Cette classe est spécialisée StylusPlugIn qui restitue le trait tel qu’il est dessiné. Le DynamicRenderer rendu sur un thread distinct permet donc à la surface d’entrée manuscrite de collecter des entrées manuscrites même lorsque le thread d’interface utilisateur de l’application est bloqué. Pour plus d’informations sur le modèle de thread, consultez Modèle de thread de l’encre. Pour personnaliser le rendu dynamique d’un trait, remplacez la OnDraw méthode.
Stroke : Implémentez une classe qui dérive de Stroke. Cette classe est responsable du rendu statique des StylusPoint données une fois qu’elles ont été converties en objet Stroke . Remplacez la méthode pour vous assurer que le DrawCore rendu statique du trait est cohérent avec le rendu dynamique.
InkCanvas : Implémentez une classe qui dérive de InkCanvas. Affectez la valeur personnalisée DynamicRenderer à la DynamicRenderer propriété. Remplacez la OnStrokeCollected méthode et ajoutez un trait personnalisé à la Strokes propriété. Cela garantit la cohérence de l’apparence de l’encre.
Implémentation d’un renderer dynamique
Bien que la DynamicRenderer classe soit une partie standard de WPF, pour effectuer un rendu plus spécialisé, vous devez créer un renderer dynamique personnalisé qui dérive de la DynamicRenderer méthode et le remplacer OnDraw .
L’exemple suivant illustre une personnalisation DynamicRenderer qui dessine l’encre avec un effet de pinceau de dégradé linéaire.
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
Implémentation de traits personnalisés
Implémentez une classe qui dérive de Stroke ; Cette classe est responsable du rendu StylusPoint des données une fois qu’elles ont été converties en objet Stroke . Remplacez la DrawCore classe pour effectuer le dessin réel.
Votre classe Stroke peut également stocker des données personnalisées à l’aide de la AddPropertyData méthode. Ces données sont stockées avec les données du trait quand elles sont rendues persistantes.
La Stroke classe peut également effectuer des tests de positionnement. Vous pouvez également implémenter votre propre algorithme de test de positionnement en remplaçant la HitTest méthode dans la classe actuelle.
Le code C# suivant illustre une classe personnalisée Stroke qui restitue les StylusPoint données sous la forme d’un trait 3D.
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
Implémentation d’un InkCanvas personnalisé
Le moyen le plus simple d’utiliser votre trait personnalisé DynamicRenderer consiste à implémenter une classe qui dérive InkCanvas de ces classes et les utilise. La InkCanvas propriété contient une DynamicRenderer propriété qui spécifie la façon dont le trait est rendu lorsque l’utilisateur le dessine.
Pour personnaliser les traits de rendu sur une InkCanvas opération , procédez comme suit :
Créez une classe qui dérive du InkCanvas.
Affectez votre valeur personnalisée DynamicRenderer à la InkCanvas.DynamicRenderer propriété.
Remplacez la méthode OnStrokeCollected . Dans cette méthode, supprimez le trait d’origine qui a été ajouté à InkCanvas. Créez ensuite un trait personnalisé, ajoutez-le à la Strokes propriété et appelez la classe de base avec un nouveau InkCanvasStrokeCollectedEventArgs trait qui contient le trait personnalisé.
Le code C# suivant illustre une classe personnalisée InkCanvas qui utilise un trait personnalisé DynamicRenderer et collecte des traits personnalisés.
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);
}
}
Un InkCanvas peut avoir plusieurs DynamicRenderer. Vous pouvez ajouter plusieurs DynamicRenderer objets à la InkCanvas propriété en les ajoutant à la StylusPlugIns propriété.
Conclusion
Vous pouvez personnaliser l’apparence de l’encre en dérivant vos propres classes Strokeet InkCanvas vos propres DynamicRendererclasses. Ensemble, ces classes garantissent que l’apparence du trait est cohérente quand l’utilisateur dessine le trait et après que celui-ci a été recueilli.
Voir aussi
.NET Desktop feedback