.net 将笔触应用于 WPF 中的文本块
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/93650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Apply stroke to a textblock in WPF
提问by Kris Erickson
How do you apply stroke (outline around text) to a textblock in xaml in WPF?
如何将笔触(文本周围的轮廓)应用于 WPF 中 xaml 中的文本块?
回答by Kent Boogaart
Below is my more idiomatically WPF, full-featured take on this. It supports pretty much everything you'd expect, including:
下面是我更惯用的 WPF,功能齐全的对此的看法。它几乎支持您所期望的一切,包括:
- all font related properties including stretch and style
- text alignment (left, right, center, justify)
- text wrapping
- text trimming
- text decorations (underline, strike through etcetera)
- 所有与字体相关的属性,包括拉伸和样式
- 文本对齐(左、右、居中、两端对齐)
- 文字环绕
- 文字修剪
- 文字装饰(下划线、删除线等)
Here's a simple example of what can be achieved with it:
这是一个简单的例子,说明可以用它实现什么:
<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>
Which results in:
结果是:


Here's the code for the 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));
}
}
回答by Javier G.
I modified the most voted answer with several fixes, including:
我通过几个修复修改了投票最多的答案,包括:
Fix so texts with a single line would show when using UseLayoutRounding.
Outlines would show outside the text instead of in the middle of the border.
The pen is created only once instead of on each render.
Fix so it won't crash when the text is set to null.
Fix so outline uses proper round caps.
修复使用 UseLayoutRounding 时显示单行文本的问题。
轮廓将显示在文本外部而不是边框中间。
笔只创建一次,而不是在每次渲染时创建。
修复它不会在文本设置为空时崩溃。
修复以便轮廓使用适当的圆帽。
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));
}
}
回答by Kris Erickson
Found It. Not so easy to do apparently, there is no built in Stroke text in WPF (kind of a big missing feature if you ask me). First create the custom class:
找到了。显然做起来并不容易,WPF 中没有内置的 Stroke 文本(如果你问我,这是一个很大的缺失功能)。首先创建自定义类:
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
}
}
The you can reference it in your xaml.
您可以在 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>
回答by codeDom
I modified @Javier G. answer
我修改了@Javier G. 答案
Stroke position can be: center, outside or Inside, the default is outside.
Fill can be transparent.
描边位置可以是:居中、外侧或内侧,默认为外侧。
填充可以是透明的。
Center:
中心:
Outside:
外部:
Inside:
里面:
Code:
代码:
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);
}
}
}
}
Usage:
用法:
<Grid Margin="12" Background="Bisque">
<local:OutlinedTextBlock Stroke="Red"
ClipToBounds="False"
FontSize="56"
Fill="Transparent"
StrokePosition="Inside"
StrokeThickness="1" Text=" abc">
</local:OutlinedTextBlock>
</Grid>
回答by SmartyP
you should wrap the TextBlock with a Border.. something like this:
你应该用边框包裹 TextBlock .. 像这样:
<Border BorderBrush="Purple" BorderThickness="2">
<TextBlock>My fancy TextBlock</TextBlock>
</Border>
in the off chance you are asking how to put a stroke around the actual letters (and not the whole TextBlock) you may want to look at using a BitmapEffect of Glow and setting the parameters on the Glow to be the stroke color you want, etc. Otherwise you may have to create something custom.
万一您问如何在实际字母(而不是整个 TextBlock)周围添加笔划,您可能需要查看使用 Glow 的 BitmapEffect 并将 Glow 上的参数设置为您想要的笔划颜色等. 否则你可能不得不创建一些自定义的东西。
回答by user00101010
as already mentioned, convert text to path
如前所述,将文本转换为路径
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;
回答by Ifeanyi Echeruo
"How to: Create Outlined Text" on MSDNhas all the information you need.
MSDN 上的“如何:创建轮廓文本”包含您需要的所有信息。
回答by Tim Erickson
In Blend you could convert the TextBlock to a Path, and then use the normal Stroke properties. But I'm assuming you wanted something that you could make dynamic...
在 Blend 中,您可以将 TextBlock 转换为 Path,然后使用普通的 Stroke 属性。但我假设你想要一些可以动态化的东西......
Otherwise I would think it would have to be some sort of bitmap effect or special brush.
否则我认为它必须是某种位图效果或特殊画笔。
回答by bwall
Another option is to use a regular Textblock, but apply a custom effect to it.
另一种选择是使用常规文本块,但对其应用自定义效果。
Combining this Shader tutorialand the Prewitt Edge Detection FilterI managed to get a decent outline effect around text. While it has the advantage of rendering using the GPU, and applying to ANY UIElement, I'd say that @Kent Boogaart's answer looks a little better, and the EdgeResponse is finicky - I had to play with it a lot to get a nice outline.
结合这个着色器教程和Prewitt 边缘检测过滤器,我设法在文本周围获得了不错的轮廓效果。虽然它具有使用 GPU 渲染并适用于任何 UIElement 的优势,但我想说@Kent Boogaart 的答案看起来更好一些,而且 EdgeResponse 很挑剔 - 我不得不经常玩它才能获得漂亮的轮廓.
The end result in XAML:
XAML 中的最终结果:
<Grid>
<Grid.Resources>
<local:EdgeDetectionEffect x:Key="OutlineEffect"
x:Shared="false"
EdgeResponse=".44"
ActualHeight="{Binding RelativeSource={RelativeSource AncestorType=TextBlock}, Path=ActualHeight}"
ActualWidth="{Binding RelativeSource={RelativeSource AncestorType=TextBlock}, Path=ActualWidth}"/>
</Grid.Resources>
<TextBlock Text="The Crazy Brown Fox Jumped Over the Lazy Dog."
FontWeight="Bold"
FontSize="25"
Foreground="Yellow"
Effect="{StaticResource OutlineEffect}"/>
</Grid>
In order to create the custom effect, I first created the EdgeDetectionColorEffect.fx (hdld) file - this is the code the GPU uses to filter the image. I compiled it in Visual Studio Command Prompt with the command:
为了创建自定义效果,我首先创建了 EdgeDetectionColorEffect.fx (hdld) 文件 - 这是 GPU 用于过滤图像的代码。我在 Visual Studio 命令提示符中使用以下命令编译它:
fxc /T ps_2_0 /E main /Focc.ps EdgeDetectionColorEffect.fx
fxc /T ps_2_0 /E main /Focc.ps EdgeDetectionColorEffect.fx
sampler2D Input : register(s0);
float ActualWidth : register(c0);
float ActualHeight : register(c1);
float4 OutlineColor : register(c2);
float EdgeDetectionResponse : register(c3);
float4 GetNeighborPixel(float2 pixelPoint, float xOffset, float yOffset)
{
float2 NeighborPoint = {pixelPoint.x + xOffset, pixelPoint.y + yOffset};
return tex2D(Input, NeighborPoint);
}
// pixel locations:
// 00 01 02
// 10 11 12
// 20 21 22
float main(float2 pixelPoint : TEXCOORD) : COLOR
{
float wo = 1 / ActualWidth; //WidthOffset
float ho = 1 / ActualHeight; //HeightOffset
float4 c00 = GetNeighborPixel(pixelPoint, -wo, -ho); // color of the pixel up and to the left of me.
float4 c01 = GetNeighborPixel(pixelPoint, 00, -ho);
float4 c02 = GetNeighborPixel(pixelPoint, wo, -ho);
float4 c10 = GetNeighborPixel(pixelPoint, -wo, 0);
float4 c11 = tex2D(Input, pixelPoint); // this is the current pixel
float4 c12 = GetNeighborPixel(pixelPoint, wo, 0);
float4 c20 = GetNeighborPixel(pixelPoint, -wo, ho);
float4 c21 = GetNeighborPixel(pixelPoint, 0, ho);
float4 c22 = GetNeighborPixel(pixelPoint, wo, ho);
float t00 = c00.r + c00.g + c00.b; //total of color channels
float t01 = c01.r + c01.g + c01.b;
float t02 = c02.r + c02.g + c02.b;
float t10 = c10.r + c10.g + c10.b;
float t11 = c11.r + c11.g + c11.b;
float t12 = c12.r + c12.g + c12.b;
float t20 = c20.r + c20.g + c20.b;
float t21 = c21.r + c21.g + c21.b;
float t22 = c22.r + c22.g + c22.b;
//Prewitt - convolve the 9 pixels with:
// 01 01 01 01 00 -1
// Gy = 00 00 00 Gx = 01 00 -1
// -1 -1 -1 01 00 -1
float gy = 0.0; float gx = 0.0;
gy += t00; gx += t00;
gy += t01; gx += t10;
gy += t02; gx += t20;
gy -= t20; gx -= t02;
gy -= t21; gx -= t12;
gy -= t22; gx -= t22;
if((gy*gy + gx*gx) > EdgeDetectionResponse)
{
return OutlineColor;
}
return c11;
}
Here's the wpf effect class:
这是 wpf 效果类:
public class EdgeDetectionEffect : ShaderEffect
{
private static PixelShader _shader = new PixelShader { UriSource = new Uri("path to your compiled shader probably called cc.ps", UriKind.Absolute) };
public EdgeDetectionEffect()
{
PixelShader = _shader;
UpdateShaderValue(InputProperty);
UpdateShaderValue(ActualHeightProperty);
UpdateShaderValue(ActualWidthProperty);
UpdateShaderValue(OutlineColorProperty);
UpdateShaderValue(EdgeResponseProperty);
}
public Brush Input
{
get => (Brush)GetValue(InputProperty);
set => SetValue(InputProperty, value);
}
public static readonly DependencyProperty InputProperty =
ShaderEffect.RegisterPixelShaderSamplerProperty(nameof(Input),
typeof(EdgeDetectionEffect), 0);
public double ActualWidth
{
get => (double)GetValue(ActualWidthProperty);
set => SetValue(ActualWidthProperty, value);
}
public static readonly DependencyProperty ActualWidthProperty =
DependencyProperty.Register(nameof(ActualWidth), typeof(double), typeof(EdgeDetectionEffect),
new UIPropertyMetadata(1.0, PixelShaderConstantCallback(0)));
public double ActualHeight
{
get => (double)GetValue(ActualHeightProperty);
set => SetValue(ActualHeightProperty, value);
}
public static readonly DependencyProperty ActualHeightProperty =
DependencyProperty.Register(nameof(ActualHeight), typeof(double), typeof(EdgeDetectionEffect),
new UIPropertyMetadata(1.0, PixelShaderConstantCallback(1)));
public Color OutlineColor
{
get => (Color)GetValue(OutlineColorProperty);
set => SetValue(OutlineColorProperty, value);
}
public static readonly DependencyProperty OutlineColorProperty=
DependencyProperty.Register(nameof(OutlineColor), typeof(Color), typeof(EdgeDetectionEffect),
new UIPropertyMetadata(Colors.Black, PixelShaderConstantCallback(2)));
public double EdgeResponse
{
get => (double)GetValue(EdgeResponseProperty);
set => SetValue(EdgeResponseProperty, value);
}
public static readonly DependencyProperty EdgeResponseProperty =
DependencyProperty.Register(nameof(EdgeResponse), typeof(double), typeof(EdgeDetectionEffect),
new UIPropertyMetadata(4.0, PixelShaderConstantCallback(3)));
}
回答by Steph M
I was trying to achieve something similar to this as well. The classes mentioned here were great, but weren't exactly what I was looking for, because it only really looked right if the text was large enough. The text I was trying to display was around 10 - 11 font size, and the stroke was so large the letters sort of blended together.
我也试图实现类似的目标。这里提到的课程很棒,但并不是我想要的,因为只有当文本足够大时,它才会真正看起来正确。我试图显示的文本大约是 10 - 11 字号,笔画太大了,字母混合在一起。
Just to clarify, this text was supposed to be overlaid on a user-defined picture, which could have varying colors, and I wanted to ensure this text would show up.
只是为了澄清,此文本应该覆盖在用户定义的图片上,该图片可能具有不同的颜色,我想确保此文本会显示出来。
I don't know if this is best practice or not, but this at least achieved the look I wanted (based on 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>
Then for the actual TextBlocks, I combined two Outer styled TextBlocks because one was too light, and one Inner styled TextBlock:
然后对于实际的 TextBlocks,我组合了两个 Outer 风格的 TextBlocks,因为一个太轻了,一个 Inner 风格的 TextBlocks:
<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>
Alternatively, you could use the DropShadowEffect, which looked okay with using only two textboxes (although adding more DropShadowEffects with varying directions and lowered opacity may look even better):
或者,您可以使用 DropShadowEffect,它只使用两个文本框看起来没问题(尽管添加更多具有不同方向和降低不透明度的 DropShadowEffect 可能看起来更好):
<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>

