Pergunta

Como você aplica acidente vascular cerebral (contorno ao redor de texto) para um bloco de texto em XAML no WPF?

Foi útil?

Solução

Abaixo está o meu mais idiomaticamente WPF, take full-featured sobre este assunto. Ele suporta praticamente tudo que você esperaria, incluindo:

  • todas as propriedades relacionadas fonte incluindo estiramento e estilo
  • alinhamento de texto (esquerda, direita, centro, justificar)
  • quebra automática de texto
  • texto aparar
  • decorações de texto (sublinhado, greve através etc.)

Aqui está um exemplo simples do que pode ser alcançado com ele:

<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>

o que resulta em:

enter descrição da imagem aqui

Aqui está o código para o controle:

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));
    }
}

Outras dicas

encontrou. Não é tão fácil de fazer, aparentemente, não há construído em texto curso em WPF (uma espécie de grande característica ausente se você me perguntar). Primeiro crie a classe 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
}
}

O que você pode fazer referência a ela em seu 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>

Eu modifiquei a resposta mais votada com várias correções, incluindo:

  • Fix tão textos com uma única linha iria mostrar ao usar UseLayoutRounding.

  • Esboços mostraria fora do texto em vez de no meio do fronteira.

  • A caneta é criada apenas uma vez, em vez de em cada render.

  • Fix por isso não irá falhar quando o texto é definido como nulo.

  • Fix tão esboço usos tampas redondas adequadas.

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));
    }
}

Eu modifiquei @Javier G. resposta

  • posição do curso pode ser: centro, fora ou dentro, o padrão está fora.

  • Fill pode ser transparente.

Center:

 enter descrição da imagem aqui

Exterior:

 enter descrição da imagem aqui

Dentro de:

 enter descrição da imagem aqui

Código:

using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Markup;
using System.Windows.Media;

namespace WpfApp2
{
    public enum StrokePosition
    {
        Center,
        Outside,
        Inside
    }

    [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
            };

            if (StrokePosition == StrokePosition.Outside || StrokePosition == StrokePosition.Inside)
            {
                _Pen.Thickness = StrokeThickness * 2;
            }

            InvalidateVisual();
        }

        public StrokePosition StrokePosition
        {
            get { return (StrokePosition)GetValue(StrokePositionProperty); }
            set { SetValue(StrokePositionProperty, value); }
        }

        public static readonly DependencyProperty StrokePositionProperty =
            DependencyProperty.Register("StrokePosition", 
                typeof(StrokePosition),
                typeof(OutlinedTextBlock),
                new FrameworkPropertyMetadata(StrokePosition.Outside, FrameworkPropertyMetadataOptions.AffectsRender));

        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;
        private Pen _Pen;
        private PathGeometry _clipGeometry;

        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(Fill, null, _TextGeometry);

            if (StrokePosition == StrokePosition.Outside)
            {
                drawingContext.PushClip(_clipGeometry);
            }
            else if (StrokePosition == StrokePosition.Inside)
            {
                drawingContext.PushClip(_TextGeometry);
            }

            drawingContext.DrawGeometry(null, _Pen, _TextGeometry);

            if (StrokePosition == StrokePosition.Outside || StrokePosition == StrokePosition.Inside)
            {
                drawingContext.Pop();
            }
        }

        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;
            UpdatePen();

            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));

            if (StrokePosition == StrokePosition.Outside)
            {
                var boundsGeo = new RectangleGeometry(new Rect(0, 0, ActualWidth, ActualHeight));
                _clipGeometry = Geometry.Combine(boundsGeo, _TextGeometry, GeometryCombineMode.Exclude, null);
            }           
        }
    }
}

Usege:

<Grid Margin="12" Background="Bisque">
    <local:OutlinedTextBlock Stroke="Red" 
                             ClipToBounds="False"
                             FontSize="56" 
                             Fill="Transparent"
                             StrokePosition="Inside"
                             StrokeThickness="1" Text=" abc">
    </local:OutlinedTextBlock>
</Grid>

Você deve envolver o TextBlock com um Border .. algo como isto:

    <Border BorderBrush="Purple" BorderThickness="2">
        <TextBlock>My fancy TextBlock</TextBlock>
    </Border>

no off chance que você está perguntando como colocar um derrame em torno das letras reais (e não todo o TextBlock) você pode querer olhar usando um BitmapEffect de brilho e definindo os parâmetros no Glow para ser o derrame cor que você quer, etc. Caso contrário, você pode ter que criar algo personalizado.

"Como criar texto esboçado" na MSDN tem todas as informações que você precisa.

Em Misture você poderia converter o TextBlock para um caminho e, em seguida, usar as propriedades normais curso. Mas eu estou supondo que você queria algo que você poderia fazer dinâmica ...

Caso contrário, eu acho que teria que ser algum tipo de efeito de bitmap ou uma escova especial.

poderia usar apenas uma etiqueta em seu lugar. Ele tem mais propriedades que você pode jogar com. Exemplo:

  <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>

Eu tive que adicionar isso para MeasureOverride para que ele iria mostrar linhas de texto enquanto estiver usando o arredondamento de layout. Ele funcionou bem quando o texto foi embrulho embora.

// return the desired size
return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height));

Se aplicado para qualquer um, aqui uma solução simples, usando apenas XAML. Eu não tenho certeza se ele tem um desempenho melhor ou pior, mas na minha opinião, parece melhor do que qualquer outra solução acima. Eu envolvê-la em um ContentControl Estilo (e modelo), seguindo este exemplo 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>

como já mencionado, convertido texto a caminho

FormattedText t = new FormattedText
(
    "abcxyz",
    CultureInfo.GetCultureInfo("en-us"),
    FlowDirection.LeftToRight,
    new Typeface(
    new FontFamily("Arial"),
    new FontStyle(),
    new FontWeight(),
    new FontStretch()),
    20,
    Brushes.Transparent
);

Geometry g = t.BuildGeometry(new System.Windows.Point(0, 0));

Path p = new Path();
p.Fill = Brushes.White;
p.Stroke = Brushes.Black;
p.StrokeThickness = 1;
p.Data = g;

não tem, intrinsecamente atributos decorativos. Eu iria colocá-lo em um com e aplicar o curso lá.

Isso me ajudou tremendamente! Apenas no caso de alguém precisar dele no futuro, aqui está a versão VB (feito StrokeThickness a uma propriedade de casal e acrescentou Sublinhado):

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

Eu estava usando solução de Kent no meu controle personalizado. Isso resultou em uma exceção nula quando usando TemplateBinding contra a propriedade de texto.

Eu tive que modificar a função MeasureOverride assim:

    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);
    }

Deve-se notar que eu não completamente testar isto.

Ligeira modificação ao código de Kent Boogaart que, embora impressionante, está faltando um pequeno detalhe. Este é provavelmente um pouco em imprecisas que ela só vai medir o preenchimento e não o derrame, mas a adição de um par de linhas para OnRender() Viewbox será capaz de obter uma alça sobre o que fazer com ele (embora, como com TextBox, não no modo de visualização ).

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);
}

Estou usando este com duas camadas de texto para que o acidente vascular cerebral parece ser apenas em torno do exterior como segue. Isto, obviamente, não vai funcionar de imediato, uma vez que se refere a imagens e fontes 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>

Eu estava tentando alcançar algo semelhante a isto também. As classes mencionadas aqui foram grandes, mas não eram exatamente o que eu estava procurando, porque realmente só parecia certo se o texto era grande o suficiente. O texto que eu estava tentando exibição foi em torno de 10-11 tamanho da fonte e o curso era tão grande as letras de classificação de conjunto misturado.

Só para esclarecer, este texto era para ser sobreposta sobre uma imagem definida pelo usuário, o que poderia ter cores variadas, e eu queria garantir este texto iria aparecer.

Eu não sei se esta é a melhor prática ou não, mas isso, pelo menos, alcançado o olhar que eu queria (baseado em este artigo ):

<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>

Então, para os TextBlocks reais, eu combinei dois Outer estilo TextBlocks porque um era muito leve, e um interior de estilo 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, você poderia usar o DropShadowEffect, que parecia bem com o uso de apenas duas caixas de texto (embora a adição de mais DropShadowEffects com diferentes direções e opacidade baixou pode parecer ainda melhor):

<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>
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top