Applying a stroke to a text block in WPF

How do you apply a stroke (outline around text) to a text block in xaml in WPF?

+58
wpf xaml
Sep 18 '08 at 15:36
source share
17 answers

Below is my more idiomatic WPF, a fully functional approach to this. It supports almost everything you expect, including:

  • all font related properties including stretch and style
  • text alignment (left, right, center, alignment)
  • text wrapping
  • text cropping
  • text decorations (underlining, breaking, etc.)

Here is a simple example of what you can achieve 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> 

Result:

enter image description here

Here is the code to 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)); } } 
+74
Mar 27 '12 at 9:50
source share

Found. Not so easy to make, apparently there is no Stroke inline text in WPF (sort of a big missing feature if you ask me). First create a custom class:

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

You can reference it in your 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> 
+32
Sep 18 '08 at 22:41
source share

I changed the most voted answer with several corrections, including:

  • Fix so that single-line texts are displayed when using UseLayoutRounding.

  • Outlines will be displayed outside the text, not in the middle of the border.

  • The pen is created only once, and not on each render.

  • Correct so that it does not work when the text is set to null.

  • It is fixed so that the styles use the correct round caps.

 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)); } } 
+30
Feb 08 '16 at 4:48
source share

I changed @Javier G. Answer

  • The stroke position can be: in the center, outside or inside, by default - outside.

  • Filling can be transparent.

Center:

enter image description here

Outside:

enter image description here

Inside:

enter image description here

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

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> 
+9
03 . '18 17:33
source share

TextBlock Border.. - :

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

, ( TextBlock), BitmapEffect of Glow Glow , .. - .

+7
18 . '08 17:40
source share
+2
15 . '09 16:57
source share

Blend TextBlock Path, Stroke. , -, ...

, - .

+1
18 . '08 16:35
source share

. , . Example:

  <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> 
+1
05 . '11 23:47
source share

-, XAML. , , , , , . ContentControl ( ), :) 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> 
+1
09 . '14 8:09
source share

MeasureOverride, . , .

 // return the desired size return new Size(Math.Ceiling(_FormattedText.Width), Math.Ceiling(_FormattedText.Height)); 
+1
08 . '16 3:15
source share

,

 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; 
+1
04 . '18 11:09
source share

<TextBlock> . <Canvas> <Rectangle> .

0
18 . '08 16:39
source share

! , - , VB ( StrokeThickness ​​ Underline):

 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 
0
22 . '11 20:09
source share

Kent Boogaart, , , . , , , , , OnRender() Viewbox , (, TextBox , ).

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

, , . , , , .

 <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> 
0
01 . '12 22:56
source share

Kent . text.

MeasureOverride :

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

, .

0
18 . '12 17:30
source share

- . , , , , , , . , , 10 - 11, , .

, , , , .

, , , , , ( )

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

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

DropShadowEffect, ( DropShadowEffects ):

 <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> 
0
10 . '14 20:47
source share

- , .

Prewitt, . , UIElement , , @Kent Boogaart , EdgeResponse - , ,

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> 

, EdgeDetectionColorEffect.fx(hdld) - , GPU . Visual Studio :

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

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))); } 
0
02 . '19 14:22
source share



All Articles