¿Cómo se aplica el trazo (contorno alrededor del texto) a un bloque de texto en xaml en WPF?Aplicar trazo a un bloque de texto en WPF
Respuesta
continuación es mi más idiomáticamente WPF, para llevar a todas las funciones de este.Es compatible con prácticamente todo lo que era de esperar, incluyendo:
- todas las propiedades relacionadas con la fuente, incluyendo estiramiento y estilo
- alineación del texto (izquierda, derecha, centro, justificar)
- ajuste de texto
- texto recorte
- decoraciones de texto (subrayado, golpean a través etcétera)
Aquí es un simple ejemplo de lo que puede lograrse con ella:
<local:OutlinedTextBlock FontFamily="Verdana" FontSize="20pt" FontWeight="ExtraBold" TextWrapping="Wrap" StrokeThickness="1" Stroke="{StaticResource TextStroke}" Fill="{StaticResource TextFill}">
Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit
</local:OutlinedTextBlock>
que se traduce en:
Aquí está el código para el control:
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
[ContentProperty("Text")]
public class OutlinedTextBlock : FrameworkElement
{
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke",
typeof(Brush),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness",
typeof(double),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextInvalidated));
public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
"TextAlignment",
typeof(TextAlignment),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register(
"TextDecorations",
typeof(TextDecorationCollection),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register(
"TextTrimming",
typeof(TextTrimming),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
"TextWrapping",
typeof(TextWrapping),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(TextWrapping.NoWrap, OnFormattedTextUpdated));
private FormattedText formattedText;
private Geometry textGeometry;
public OutlinedTextBlock()
{
this.TextDecorations = new TextDecorationCollection();
}
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public TextAlignment TextAlignment
{
get { return (TextAlignment)GetValue(TextAlignmentProperty); }
set { SetValue(TextAlignmentProperty, value); }
}
public TextDecorationCollection TextDecorations
{
get { return (TextDecorationCollection)this.GetValue(TextDecorationsProperty); }
set { this.SetValue(TextDecorationsProperty, value); }
}
public TextTrimming TextTrimming
{
get { return (TextTrimming)GetValue(TextTrimmingProperty); }
set { SetValue(TextTrimmingProperty, value); }
}
public TextWrapping TextWrapping
{
get { return (TextWrapping)GetValue(TextWrappingProperty); }
set { SetValue(TextWrappingProperty, value); }
}
protected override void OnRender(DrawingContext drawingContext)
{
this.EnsureGeometry();
drawingContext.DrawGeometry(this.Fill, new Pen(this.Stroke, this.StrokeThickness), this.textGeometry);
}
protected override Size MeasureOverride(Size availableSize)
{
this.EnsureFormattedText();
// constrain the formatted text according to the available size
// the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
// the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw
this.formattedText.MaxTextWidth = Math.Min(3579139, availableSize.Width);
this.formattedText.MaxTextHeight = Math.Max(0.0001d, availableSize.Height);
// return the desired size
return new Size(this.formattedText.Width, this.formattedText.Height);
}
protected override Size ArrangeOverride(Size finalSize)
{
this.EnsureFormattedText();
// update the formatted text with the final size
this.formattedText.MaxTextWidth = finalSize.Width;
this.formattedText.MaxTextHeight = finalSize.Height;
// need to re-generate the geometry now that the dimensions have changed
this.textGeometry = null;
return finalSize;
}
private static void OnFormattedTextInvalidated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
outlinedTextBlock.formattedText = null;
outlinedTextBlock.textGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
private static void OnFormattedTextUpdated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
outlinedTextBlock.UpdateFormattedText();
outlinedTextBlock.textGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
private void EnsureFormattedText()
{
if (this.formattedText != null || this.Text == null)
{
return;
}
this.formattedText = new FormattedText(
this.Text,
CultureInfo.CurrentUICulture,
this.FlowDirection,
new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, FontStretches.Normal),
this.FontSize,
Brushes.Black);
this.UpdateFormattedText();
}
private void UpdateFormattedText()
{
if (this.formattedText == null)
{
return;
}
this.formattedText.MaxLineCount = this.TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue;
this.formattedText.TextAlignment = this.TextAlignment;
this.formattedText.Trimming = this.TextTrimming;
this.formattedText.SetFontSize(this.FontSize);
this.formattedText.SetFontStyle(this.FontStyle);
this.formattedText.SetFontWeight(this.FontWeight);
this.formattedText.SetFontFamily(this.FontFamily);
this.formattedText.SetFontStretch(this.FontStretch);
this.formattedText.SetTextDecorations(this.TextDecorations);
}
private void EnsureGeometry()
{
if (this.textGeometry != null)
{
return;
}
this.EnsureFormattedText();
this.textGeometry = this.formattedText.BuildGeometry(new Point(0, 0));
}
}
En Blend puede convertir TextBlock en una ruta, y luego usar las propiedades normales de trazo. Pero supongo que querías algo que pudieras hacer dinámico ...
De lo contrario, creo que tendría que ser algún tipo de efecto de mapa de bits o pincel especial.
<TextBlock> no tiene atributos decorativos en sí. Lo pondría en un <Lienzo> con un < Rectángulo > y aplicaré el trazo allí.
@ kaiz.net: Frontera! BESO. – user7116
que debe envolver el TextBlock con un borde .. algo como esto:
<Border BorderBrush="Purple" BorderThickness="2">
<TextBlock>My fancy TextBlock</TextBlock>
</Border>
en la remota posibilidad de que se preguntan cómo poner un derrame cerebral alrededor de las letras reales (y no todo el TextBlock) que puede quiero ver el uso de un efecto BitmapEffect de Glow y establecer los parámetros en el Glow para que sea el color de trazo que desee, etc. De lo contrario, tendrá que crear algo personalizado.
Found It. No es tan fácil de hacer, al parecer, no hay texto de trazo incorporado en WPF (tipo de una gran característica que falta si me preguntas). Primero cree la clase personalizada:
using System;
using System.Windows.Media;
using System.Globalization;
using System.Windows;
using System.Windows.Markup;
namespace CustomXaml
{
public class OutlinedText : FrameworkElement, IAddChild
{
#region Private Fields
private Geometry _textGeometry;
#endregion
#region Private Methods
/// <summary>
/// Invoked when a dependency property has changed. Generate a new FormattedText object to display.
/// </summary>
/// <param name="d">OutlineText object whose property was updated.</param>
/// <param name="e">Event arguments for the dependency property.</param>
private static void OnOutlineTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((OutlinedText)d).CreateText();
}
#endregion
#region FrameworkElement Overrides
/// <summary>
/// OnRender override draws the geometry of the text and optional highlight.
/// </summary>
/// <param name="drawingContext">Drawing context of the OutlineText control.</param>
protected override void OnRender(DrawingContext drawingContext)
{
CreateText();
// Draw the outline based on the properties that are set.
drawingContext.DrawGeometry(Fill, new Pen(Stroke, StrokeThickness), _textGeometry);
}
/// <summary>
/// Create the outline geometry based on the formatted text.
/// </summary>
public void CreateText()
{
FontStyle fontStyle = FontStyles.Normal;
FontWeight fontWeight = FontWeights.Medium;
if (Bold == true) fontWeight = FontWeights.Bold;
if (Italic == true) fontStyle = FontStyles.Italic;
// Create the formatted text based on the properties set.
FormattedText formattedText = new FormattedText(
Text,
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface(Font, fontStyle, fontWeight, FontStretches.Normal),
FontSize,
Brushes.Black // This brush does not matter since we use the geometry of the text.
);
// Build the geometry object that represents the text.
_textGeometry = formattedText.BuildGeometry(new Point(0, 0));
//set the size of the custome control based on the size of the text
this.MinWidth = formattedText.Width;
this.MinHeight = formattedText.Height;
}
#endregion
#region DependencyProperties
/// <summary>
/// Specifies whether the font should display Bold font weight.
/// </summary>
public bool Bold
{
get
{
return (bool)GetValue(BoldProperty);
}
set
{
SetValue(BoldProperty, value);
}
}
/// <summary>
/// Identifies the Bold dependency property.
/// </summary>
public static readonly DependencyProperty BoldProperty = DependencyProperty.Register(
"Bold",
typeof(bool),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// Specifies the brush to use for the fill of the formatted text.
/// </summary>
public Brush Fill
{
get
{
return (Brush)GetValue(FillProperty);
}
set
{
SetValue(FillProperty, value);
}
}
/// <summary>
/// Identifies the Fill dependency property.
/// </summary>
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
new SolidColorBrush(Colors.LightSteelBlue),
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// The font to use for the displayed formatted text.
/// </summary>
public FontFamily Font
{
get
{
return (FontFamily)GetValue(FontProperty);
}
set
{
SetValue(FontProperty, value);
}
}
/// <summary>
/// Identifies the Font dependency property.
/// </summary>
public static readonly DependencyProperty FontProperty = DependencyProperty.Register(
"Font",
typeof(FontFamily),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
new FontFamily("Arial"),
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// The current font size.
/// </summary>
public double FontSize
{
get
{
return (double)GetValue(FontSizeProperty);
}
set
{
SetValue(FontSizeProperty, value);
}
}
/// <summary>
/// Identifies the FontSize dependency property.
/// </summary>
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
"FontSize",
typeof(double),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
(double)48.0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// Specifies whether the font should display Italic font style.
/// </summary>
public bool Italic
{
get
{
return (bool)GetValue(ItalicProperty);
}
set
{
SetValue(ItalicProperty, value);
}
}
/// <summary>
/// Identifies the Italic dependency property.
/// </summary>
public static readonly DependencyProperty ItalicProperty = DependencyProperty.Register(
"Italic",
typeof(bool),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
false,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// Specifies the brush to use for the stroke and optional hightlight of the formatted text.
/// </summary>
public Brush Stroke
{
get
{
return (Brush)GetValue(StrokeProperty);
}
set
{
SetValue(StrokeProperty, value);
}
}
/// <summary>
/// Identifies the Stroke dependency property.
/// </summary>
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke",
typeof(Brush),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
new SolidColorBrush(Colors.Teal),
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// The stroke thickness of the font.
/// </summary>
public ushort StrokeThickness
{
get
{
return (ushort)GetValue(StrokeThicknessProperty);
}
set
{
SetValue(StrokeThicknessProperty, value);
}
}
/// <summary>
/// Identifies the StrokeThickness dependency property.
/// </summary>
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness",
typeof(ushort),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
(ushort)0,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
/// <summary>
/// Specifies the text string to display.
/// </summary>
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
/// <summary>
/// Identifies the Text dependency property.
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(OutlinedText),
new FrameworkPropertyMetadata(
"",
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnOutlineTextInvalidated),
null
)
);
public void AddChild(Object value)
{
}
public void AddText(string value)
{
Text = value;
}
#endregion
}
}
La puede hacer referencia en su xaml.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:customControls="clr-namespace:CustomXaml;assembly=CustomXaml">
<Grid>
<customControls:OutlinedText x:Name="TextContent" Fill="#ffffffff" FontSize="28"
Bold="True" Stroke="Black" StrokeThickness="1" Text="Back" Margin="10,0,10,0"
HorizontalAlignment="Center" VerticalAlignment="Center" Height="Auto" Width="Auto" />
</Grid>
</Page>
Esto funcionó muy bien para mí, pero también necesitaba el ajuste de texto y alineación de texto. Esto se hace fácilmente usando las propiedades FormattedText.MaxTextWidth y FormattedText.TextAlignment. – Twelve47
¡Impresionante! Tenga en cuenta que IAddChild está en desuso. En su lugar, atribuya la clase con [ContentProperty ("Texto")], luego puede establecer el texto directamente en XAML como en:
Esto es increíble. Dado que esta respuesta es un poco vieja ahora, ¿hay alguna otra forma de lograr esto o este sigue siendo el mejor enfoque? +1 –
"How to: Create Outlined Text" on MSDN tiene toda la información que necesita.
Podría simplemente usar una etiqueta en su lugar. Tiene más propiedades con las que puedes jugar. Ejemplo:
<Style x:Key="LeftBorderLabel" TargetType="{x:Type Label}">
<Setter Property="Margin" Value="0" />
<Setter Property="BorderThickness" Value="1,0,0,0" />
<Setter Property="BorderBrush" Value="Blue" />
</Style>
¡Esto me ayudó muchísimo! Por si acaso alguien lo necesita en el futuro, aquí está la versión VB (hecho StrokeThickness un doble y ha añadido una propiedad subrayado):
Imports System
Imports System.Windows.Media
Imports System.Globalization
Imports System.Windows
Imports System.Windows.Markup
Namespace CustomXaml
Public Class OutlinedText
Inherits FrameworkElement
Implements IAddChild
Private _textGeometry As Geometry
Private Shared Sub OnOutlineTextInvalidated(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
DirectCast(d, OutlinedText).CreateText()
End Sub
Protected Overrides Sub OnRender(drawingContext As System.Windows.Media.DrawingContext)
CreateText()
drawingContext.DrawGeometry(Fill, New Pen(Stroke, StrokeThickness), _textGeometry)
End Sub
Public Sub CreateText()
Dim fontStyle = FontStyles.Normal
Dim fontWeight = FontWeights.Medium
Dim fontDecoration = New TextDecorationCollection()
If Bold Then fontWeight = FontWeights.Bold
If Italic Then fontStyle = FontStyles.Italic
If Underline Then fontDecoration.Add(TextDecorations.Underline)
Dim formattedText = New FormattedText(_
Text, _
CultureInfo.GetCultureInfo("en-us"), _
FlowDirection.LeftToRight, _
New Typeface(Font, fontStyle, fontWeight, FontStretches.Normal), _
FontSize, _
Brushes.Black _
)
formattedText.SetTextDecorations(fontDecoration)
_textGeometry = formattedText.BuildGeometry(New Point(0, 0))
Me.MinWidth = formattedText.Width
Me.MinHeight = formattedText.Height
End Sub
Public Property Bold As Boolean
Get
Return CType(GetValue(BoldProperty), Boolean)
End Get
Set(value As Boolean)
SetValue(BoldProperty, value)
End Set
End Property
Public Shared ReadOnly BoldProperty As DependencyProperty = DependencyProperty.Register(_
"Bold", _
GetType(Boolean), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
False, _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Underline As Boolean
Get
Return CType(GetValue(UnderlineProperty), Boolean)
End Get
Set(value As Boolean)
SetValue(UnderlineProperty, value)
End Set
End Property
Public Shared ReadOnly UnderlineProperty As DependencyProperty = DependencyProperty.Register(_
"Underline", _
GetType(Boolean), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
False, _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Fill As Brush
Get
Return CType(GetValue(FillProperty), Brush)
End Get
Set(value As Brush)
SetValue(FillProperty, value)
End Set
End Property
Public Shared ReadOnly FillProperty As DependencyProperty = DependencyProperty.Register(_
"Fill", _
GetType(Brush), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
New SolidColorBrush(Colors.LightSteelBlue), _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Font As FontFamily
Get
Return CType(GetValue(FontProperty), FontFamily)
End Get
Set(value As FontFamily)
SetValue(FontProperty, value)
End Set
End Property
Public Shared ReadOnly FontProperty As DependencyProperty = DependencyProperty.Register(_
"Font", _
GetType(FontFamily), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
New FontFamily("Arial"), _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property FontSize As Double
Get
Return CType(GetValue(FontSizeProperty), Double)
End Get
Set(value As Double)
SetValue(FontSizeProperty, value)
End Set
End Property
Public Shared ReadOnly FontSizeProperty As DependencyProperty = DependencyProperty.Register(_
"FontSize", _
GetType(Double), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
CDbl(48.0), _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Italic As Boolean
Get
Return CType(GetValue(ItalicProperty), Boolean)
End Get
Set(value As Boolean)
SetValue(ItalicProperty, value)
End Set
End Property
Public Shared ReadOnly ItalicProperty As DependencyProperty = DependencyProperty.Register(_
"Italic", _
GetType(Boolean), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
False, _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Stroke As Brush
Get
Return CType(GetValue(StrokeProperty), Brush)
End Get
Set(value As Brush)
SetValue(StrokeProperty, value)
End Set
End Property
Public Shared ReadOnly StrokeProperty As DependencyProperty = DependencyProperty.Register(_
"Stroke", _
GetType(Brush), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
New SolidColorBrush(Colors.Teal), _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property StrokeThickness As Double
Get
Return CType(GetValue(StrokeThicknessProperty), Double)
End Get
Set(value As Double)
SetValue(StrokeThicknessProperty, value)
End Set
End Property
Public Shared ReadOnly StrokeThicknessProperty As DependencyProperty = DependencyProperty.Register(_
"StrokeThickness", _
GetType(Double), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
CDbl(0), _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Property Text As String
Get
Return CType(GetValue(TextProperty), String)
End Get
Set(value As String)
SetValue(TextProperty, value)
End Set
End Property
Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register(_
"Text", _
GetType(String), _
GetType(OutlinedText), _
New FrameworkPropertyMetadata(_
"", _
FrameworkPropertyMetadataOptions.AffectsRender, _
New PropertyChangedCallback(AddressOf OnOutlineTextInvalidated), _
Nothing _
) _
)
Public Sub AddChild(value As Object) Implements System.Windows.Markup.IAddChild.AddChild
End Sub
Public Sub AddText(text As String) Implements System.Windows.Markup.IAddChild.AddText
Me.Text = text
End Sub
End Class
End Namespace
Ligera modificación al código de Kent Boogaart que, aunque impresionante, le falta un pequeño detalle . Esto es probablemente un poco impreciso, ya que solo medirá el llenado y no el trazo, pero al agregar un par de líneas al OnRender()
, el Viewbox
podrá obtener una idea de qué hacer con él (aunque, como con TextBox
, no en la vista previa)
protected override void OnRender(DrawingContext drawingContext)
{
this.EnsureGeometry();
this.Width = this.formattedText.Width;
this.Height = this.formattedText.Height;
drawingContext.DrawGeometry(this.Fill, new Pen(this.Stroke, this.StrokeThickness), this.textGeometry);
}
estoy usando esto con dos capas de texto a fin de la carrera parece ser solamente alrededor del exterior de la siguiente manera. Evidentemente, esto no funcionará de inmediato ya que se refiere a imágenes y fuentes específicas.
<Viewbox Stretch="UniformToFill" Margin="0" Grid.Column="1">
<bd:OutlinedText x:Name="LevelTitleStroke" Text="Level" FontSize="80pt" FontFamily="/fonts/papercuts-2.ttf#Paper Cuts 2" Grid.Row="1" TextAlignment="Center" IsHitTestVisible="False" StrokeThickness="15">
<bd:OutlinedText.Stroke>
<ImageBrush ImageSource="/WpfApplication1;component/GrungeMaps/03DarkBlue.jpg" Stretch="None" />
</bd:OutlinedText.Stroke>
</bd:OutlinedText>
</Viewbox>
<Viewbox Stretch="UniformToFill" Margin="0" Grid.Column="1">
<bd:OutlinedText x:Name="LevelTitleFill" Text="Level" FontSize="80pt" FontFamily="/fonts/papercuts-2.ttf#Paper Cuts 2" Grid.Row="1" TextAlignment="Center" IsHitTestVisible="False">
<bd:OutlinedText.Fill>
<ImageBrush ImageSource="/WpfApplication1;component/GrungeMaps/03Red.jpg" Stretch="None" />
</bd:OutlinedText.Fill>
</bd:OutlinedText>
</Viewbox>
Estaba usando la solución de Kent en mi control personalizado. Resultó en una excepción nula cuando se usa la vinculación de plantilla con la propiedad de texto.
tuve que modificar la función MeasureOverride así:
protected override Size MeasureOverride(Size availableSize)
{
this.EnsureFormattedText();
if (this.formattedText == null)
{
this.formattedText = new FormattedText(
(this.Text == null) ? "" : this.Text,
CultureInfo.CurrentUICulture,
this.FlowDirection,
new Typeface(this.FontFamily, this.FontStyle, this.FontWeight, FontStretches.Normal),
this.FontSize,
Brushes.Black);
}
// constrain the formatted text according to the available size
// the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
this.formattedText.MaxTextWidth = Math.Min(3579139, availableSize.Width);
this.formattedText.MaxTextHeight = availableSize.Height;
// return the desired size
return new Size(this.formattedText.Width, this.formattedText.Height);
}
Debe tenerse en cuenta que no he probado a fondo esto.
Estaba tratando de lograr algo similar a esto también. Las clases mencionadas aquí fueron geniales, pero no eran exactamente lo que estaba buscando, porque solo parecía correcto si el texto era lo suficientemente grande. El texto que intentaba mostrar tenía un tamaño de letra de 10 a 11, y el trazo era tan grande que las letras se mezclaron.
Solo para aclarar, se suponía que este texto se superponía en una imagen definida por el usuario, que podía tener colores diferentes, y quería asegurarme de que este texto apareciera.
No sé si esta es la mejor práctica o no, pero esto por lo menos conseguir el aspecto que quería (basado en this article):
<Style x:Key="OutlinedTextBlockOuter" TargetType="TextBlock">
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontSize" Value="10"/>
<Setter Property="Effect">
<Setter.Value>
<BlurEffect Radius="3.0"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="OutlinedTextBlockInner" TargetType="TextBlock">
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="10"/>
</Style>
Entonces, para los TextBlocks reales, he combinado dos exteriores LABRÓ TextBlocks porque uno era demasiado ligero, y uno interior labrado TextBlock:
<Grid Margin="5">
<TextBlock Style="{StaticResource OutlinedTextBlockOuter}" Text="This is outlined text using BlurEffect"/>
<TextBlock Style="{StaticResource OutlinedTextBlockOuter}" Text="This is outlined text using BlurEffect"/>
<TextBlock Style="{StaticResource OutlinedTextBlockInner}" Text="This is outlined text using BlurEffect"/>
</Grid>
Como alternativa, puede utilizar el DropShadowEffect, que se veía bien con el uso de sólo dos cuadros de texto (aunque la adición de más DropShadowEffects con direcciones diferentes y bajó opacit Y pueden ver aún mejor):
<Grid Margin="5">
<TextBlock Text="This is my outlined text using the DropShadowEffect" FontSize="10" Foreground="White">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="2" Opacity="0.75" Direction="315"/>
</TextBlock.Effect>
</TextBlock>
<TextBlock Text="This is my outlined text using the DropShadowEffect" FontSize="10" Foreground="White">
<TextBlock.Effect>
<DropShadowEffect ShadowDepth="1" BlurRadius="2" Opacity="0.75" Direction="135"/>
</TextBlock.Effect>
</TextBlock>
</Grid>
Si se aplica para cualquier persona, aquí una solución sencilla utilizando sólo XAML, no Sureif mejor o peor en cuanto a rendimiento, pero para asegurarse de que se vea mejor que cualquier otra solución anterior (en mi caso). I Warp en ContentContro
l Estilo (y plantilla), siguiendo este ejemplo Old School :) http://oldschooldotnet.blogspot.co.il/2009/02/fancy-fonts-in-xaml-silverlight-and-wpf.html
<Style x:Key="OutlinedText" TargetType="{x:Type ContentControl}">
<!-- Some Style Setters -->
<Setter Property="Content" Value="Outlined Text"/>
<Setter Property="Padding" Value="0"/>
<!-- Border Brush Must be equal '0' because TextBlock that emulate the stroke will using the BorderBrush as to define 'Stroke' color-->
<Setter Property="BorderThickness" Value="0"/>
<!-- Border Brush define 'Stroke' Color-->
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontFamily" Value="Seoge UI Bold"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Canvas Width="{Binding ActualWidth, ElementName=FillText}" Height="{Binding ActualHeight, ElementName=FillText}">
<Canvas.Resources>
<!-- Style to ease the duplication of Text Blocks that emulate the stroke: Binding to one element (or to template) is the first part of the Trick -->
<Style x:Key="OutlinedTextStrokeTextBlock_Style" TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding Text, ElementName=FillText}"/>
<Setter Property="FontSize" Value="{Binding FontSize, ElementName=FillText}"/>
<Setter Property="FontFamily" Value="{Binding FontFamily, ElementName=FillText}"/>
<Setter Property="FontStyle" Value="{Binding FontStyle, ElementName=FillText}"/>
<Setter Property="FontWeight" Value="{Binding FontWeight, ElementName=FillText}"/>
<Setter Property="Padding" Value="{Binding TextAlignment, ElementName=Padding}"/>
<Setter Property="TextAlignment" Value="{Binding TextAlignment, ElementName=FillText}"/>
<Setter Property="VerticalAlignment" Value="{Binding VerticalAlignment, ElementName=FillText}"/>
</Style>
</Canvas.Resources>
<!-- Offseting the Text block will create the outline, the margin is the Stroke Width-->
<TextBlock Foreground="{TemplateBinding BorderBrush}" Margin="-1,0,0,0" Style="{DynamicResource OutlinedTextStrokeTextBlock_Style}"/>
<TextBlock Foreground="{TemplateBinding BorderBrush}" Margin="0,-1,0,0" Style="{DynamicResource OutlinedTextStrokeTextBlock_Style}"/>
<TextBlock Foreground="{TemplateBinding BorderBrush}" Margin="0,0,-1,0" Style="{DynamicResource OutlinedTextStrokeTextBlock_Style}"/>
<TextBlock Foreground="{TemplateBinding BorderBrush}" Margin="0,0,0,-1" Style="{DynamicResource OutlinedTextStrokeTextBlock_Style}"/>
<!-- Base TextBlock Will be the Fill -->
<TextBlock x:Name="FillText" Text="{TemplateBinding Content}" FontSize="{TemplateBinding FontSize}" FontFamily="{TemplateBinding FontFamily}"
FontStyle="{TemplateBinding FontStyle}" FontWeight="{TemplateBinding FontWeight}" Padding="0" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
TextAlignment="{TemplateBinding HorizontalContentAlignment}"
Style="{DynamicResource TbMediaOverlay_Style}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
he tenido que añadir a esta MeasureOverride por lo que mostraría líneas individuales de texto mientras se utiliza el redondeo del diseño. Sin embargo, funcionó bien cuando el texto se estaba completando.
// return the desired size
return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height));
que modifica la respuesta más votado con varias correcciones, incluyendo:
Fix tan textos con una sola línea mostrarían al utilizar UseLayoutRounding.
Los contornos se mostrarían fuera del texto en lugar de en el medio del borde .
El lápiz se crea solo una vez en lugar de en cada renderizado.
Repare para que no se bloquee cuando el texto se establece en nulo.
Reordene el contorno para usar tapas redondas adecuadas.
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;
[ContentProperty("Text")]
public class OutlinedTextBlock : FrameworkElement
{
private void UpdatePen() {
_Pen = new Pen(Stroke, StrokeThickness) {
DashCap = PenLineCap.Round,
EndLineCap = PenLineCap.Round,
LineJoin = PenLineJoin.Round,
StartLineCap = PenLineCap.Round
};
InvalidateVisual();
}
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
"Fill",
typeof(Brush),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender));
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
"Stroke",
typeof(Brush),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
private static void StrokePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs) {
(dependencyObject as OutlinedTextBlock)?.UpdatePen();
}
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
"StrokeThickness",
typeof(double),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(1d, FrameworkPropertyMetadataOptions.AffectsRender, StrokePropertyChangedCallback));
public static readonly DependencyProperty FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontSizeProperty = TextElement.FontSizeProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontStretchProperty = TextElement.FontStretchProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontStyleProperty = TextElement.FontStyleProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty FontWeightProperty = TextElement.FontWeightProperty.AddOwner(
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextInvalidated));
public static readonly DependencyProperty TextAlignmentProperty = DependencyProperty.Register(
"TextAlignment",
typeof(TextAlignment),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextDecorationsProperty = DependencyProperty.Register(
"TextDecorations",
typeof(TextDecorationCollection),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextTrimmingProperty = DependencyProperty.Register(
"TextTrimming",
typeof(TextTrimming),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(OnFormattedTextUpdated));
public static readonly DependencyProperty TextWrappingProperty = DependencyProperty.Register(
"TextWrapping",
typeof(TextWrapping),
typeof(OutlinedTextBlock),
new FrameworkPropertyMetadata(TextWrapping.NoWrap, OnFormattedTextUpdated));
private FormattedText _FormattedText;
private Geometry _TextGeometry;
private Pen _Pen;
public Brush Fill
{
get { return (Brush)GetValue(FillProperty); }
set { SetValue(FillProperty, value); }
}
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
[TypeConverter(typeof(FontSizeConverter))]
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
public Brush Stroke
{
get { return (Brush)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public TextAlignment TextAlignment
{
get { return (TextAlignment)GetValue(TextAlignmentProperty); }
set { SetValue(TextAlignmentProperty, value); }
}
public TextDecorationCollection TextDecorations
{
get { return (TextDecorationCollection)GetValue(TextDecorationsProperty); }
set { SetValue(TextDecorationsProperty, value); }
}
public TextTrimming TextTrimming
{
get { return (TextTrimming)GetValue(TextTrimmingProperty); }
set { SetValue(TextTrimmingProperty, value); }
}
public TextWrapping TextWrapping
{
get { return (TextWrapping)GetValue(TextWrappingProperty); }
set { SetValue(TextWrappingProperty, value); }
}
public OutlinedTextBlock() {
UpdatePen();
TextDecorations = new TextDecorationCollection();
}
protected override void OnRender(DrawingContext drawingContext) {
EnsureGeometry();
drawingContext.DrawGeometry(null, _Pen, _TextGeometry);
drawingContext.DrawGeometry(Fill, null, _TextGeometry);
}
protected override Size MeasureOverride(Size availableSize) {
EnsureFormattedText();
// constrain the formatted text according to the available size
double w = availableSize.Width;
double h = availableSize.Height;
// the Math.Min call is important - without this constraint (which seems arbitrary, but is the maximum allowable text width), things blow up when availableSize is infinite in both directions
// the Math.Max call is to ensure we don't hit zero, which will cause MaxTextHeight to throw
_FormattedText.MaxTextWidth = Math.Min(3579139, w);
_FormattedText.MaxTextHeight = Math.Max(0.0001d, h);
// return the desired size
return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height));
}
protected override Size ArrangeOverride(Size finalSize) {
EnsureFormattedText();
// update the formatted text with the final size
_FormattedText.MaxTextWidth = finalSize.Width;
_FormattedText.MaxTextHeight = Math.Max(0.0001d, finalSize.Height);
// need to re-generate the geometry now that the dimensions have changed
_TextGeometry = null;
return finalSize;
}
private static void OnFormattedTextInvalidated(DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e) {
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
outlinedTextBlock._FormattedText = null;
outlinedTextBlock._TextGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
private static void OnFormattedTextUpdated(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) {
var outlinedTextBlock = (OutlinedTextBlock)dependencyObject;
outlinedTextBlock.UpdateFormattedText();
outlinedTextBlock._TextGeometry = null;
outlinedTextBlock.InvalidateMeasure();
outlinedTextBlock.InvalidateVisual();
}
private void EnsureFormattedText() {
if (_FormattedText != null) {
return;
}
_FormattedText = new FormattedText(
Text ?? "",
CultureInfo.CurrentUICulture,
FlowDirection,
new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
FontSize,
Brushes.Black);
UpdateFormattedText();
}
private void UpdateFormattedText() {
if (_FormattedText == null) {
return;
}
_FormattedText.MaxLineCount = TextWrapping == TextWrapping.NoWrap ? 1 : int.MaxValue;
_FormattedText.TextAlignment = TextAlignment;
_FormattedText.Trimming = TextTrimming;
_FormattedText.SetFontSize(FontSize);
_FormattedText.SetFontStyle(FontStyle);
_FormattedText.SetFontWeight(FontWeight);
_FormattedText.SetFontFamily(FontFamily);
_FormattedText.SetFontStretch(FontStretch);
_FormattedText.SetTextDecorations(TextDecorations);
}
private void EnsureGeometry() {
if (_TextGeometry != null) {
return;
}
EnsureFormattedText();
_TextGeometry = _FormattedText.BuildGeometry(new Point(0, 0));
}
}
- 1. ¿Cómo hacer que un bloque de texto parpadee en wpf?
- 2. Aplicar UpdateSourceTrigger = PropertyChanged a todos los cuadros de texto wpf
- 3. ¿Cómo agregar un borde al texto dentro de un bloque de texto en WPF?
- 4. Crear trazo de texto para UILabel iphone
- 5. Aplicar un degradado a CAShapeLayer
- 6. Cómo aplicar sombra de texto a UITextView?
- 7. Aplicar estilo WPF predeterminado
- 8. WPF: Volver a aplicar DataTemplateSelector cuando un cierto valor cambia
- 9. Vertical Alinear texto en un bloque <a> tag
- 10. WPF - ¿Cómo alinear correctamente un bloque de texto dentro de un panel de distribución orientado horizontalmente?
- 11. Cómo aplicar estilo de disparo a DataTemplate en WPF
- 12. ¿Cuál es la mejor manera de obtener párrafos en un bloque de texto WPF? (caracteres Newline?)
- 13. PHP Echo un gran bloque de texto
- 14. Aplicar el modificador Smarty en la salida de bloque
- 15. Transparencia de un trazo lleno en HTML5
- 16. Cómo dibujar texto con diferentes colores de trazo y relleno?
- 17. ¿Cómo hago un trazo en el iPhone?
- 18. WPF Agregar un borde a un TextBlock
- 19. Formato de texto WPF en GridViewColumn
- 20. Bloque largo de texto en Jade textarea?
- 21. Ajuste de texto WPF a TextBlock
- 22. Android: trazo en una forma crear un margen del ancho de trazo
- 23. Cómo seleccionar texto en el bloque de texto Silverlight 3.0
- 24. Wpf: Arrastrar y soltar a un cuadro de texto
- 25. Aplicar color ARGB a una vista de texto programáticamente
- 26. cómo agregar CharacterEllipsis al comienzo del bloque de texto en WPF?
- 27. ¿Cómo simulo la entrada de texto a un WPF TextBox?
- 28. Efecto de bisel externo en el texto en WPF
- 29. texto de medición en WPF
- 30. En OSX, ¿cómo llento un degradado un trazo de ruta?
esto sería fantástico si el trazo estuviera fuera de las letras. Gracias por compartir. – discorax
Al usar TemplateBinding con la propiedad Text obtengo una excepción de objeto nulo. Parece ser de la función MeasureOverride donde formattedText es nulo –
Agregué un esto inmediatamente después de this.EnsureFormattedText(); en MeasureOverride –