123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- using System;
- using System.ComponentModel;
- using System.Globalization;
- using System.Windows;
- using System.Windows.Documents;
- using System.Windows.Markup;
- using System.Windows.Media;
- namespace DesktopClock;
- // https://stackoverflow.com/a/35262509
- [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 => (Brush)GetValue(FillProperty);
- set => SetValue(FillProperty, value);
- }
- public FontFamily FontFamily
- {
- get => (FontFamily)GetValue(FontFamilyProperty);
- set => SetValue(FontFamilyProperty, value);
- }
- [TypeConverter(typeof(FontSizeConverter))]
- public double FontSize
- {
- get => (double)GetValue(FontSizeProperty);
- set => SetValue(FontSizeProperty, value);
- }
- public FontStretch FontStretch
- {
- get => (FontStretch)GetValue(FontStretchProperty);
- set => SetValue(FontStretchProperty, value);
- }
- public FontStyle FontStyle
- {
- get => (FontStyle)GetValue(FontStyleProperty);
- set => SetValue(FontStyleProperty, value);
- }
- public FontWeight FontWeight
- {
- get => (FontWeight)GetValue(FontWeightProperty);
- set => SetValue(FontWeightProperty, value);
- }
- public Brush Stroke
- {
- get => (Brush)GetValue(StrokeProperty);
- set => SetValue(StrokeProperty, value);
- }
- public double StrokeThickness
- {
- get => (double)GetValue(StrokeThicknessProperty);
- set => SetValue(StrokeThicknessProperty, value);
- }
- public string Text
- {
- get => (string)GetValue(TextProperty);
- set => SetValue(TextProperty, value);
- }
- public TextAlignment TextAlignment
- {
- get => (TextAlignment)GetValue(TextAlignmentProperty);
- set => SetValue(TextAlignmentProperty, value);
- }
- public TextDecorationCollection TextDecorations
- {
- get => (TextDecorationCollection)GetValue(TextDecorationsProperty);
- set => SetValue(TextDecorationsProperty, value);
- }
- public TextTrimming TextTrimming
- {
- get => (TextTrimming)GetValue(TextTrimmingProperty);
- set => SetValue(TextTrimmingProperty, value);
- }
- public TextWrapping TextWrapping
- {
- get => (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
- var w = availableSize.Width;
- var 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;
- }
- #pragma warning disable CS0618 // Type or member is obsolete
- _FormattedText = new FormattedText(
- Text ?? "",
- CultureInfo.CurrentUICulture,
- FlowDirection,
- new Typeface(FontFamily, FontStyle, FontWeight, FontStretch),
- FontSize,
- Brushes.Black);
- #pragma warning restore CS0618 // Type or member is obsolete
- 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));
- }
- }
|