using System; using System.ComponentModel; using System.Globalization; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace OperationControl.Controls { public class AutoFitTextBlock : TextBlock { public static readonly DependencyProperty MinFontSizeProperty = DependencyProperty.Register( nameof(MinFontSize), typeof(double), typeof(AutoFitTextBlock), new PropertyMetadata(10.0, OnAutoFitPropertyChanged)); public static readonly DependencyProperty MaxLinesProperty = DependencyProperty.Register( nameof(MaxLines), typeof(int), typeof(AutoFitTextBlock), new PropertyMetadata(2, OnAutoFitPropertyChanged)); private double _baseFontSize = 14; private bool _isAdjusting; public double MinFontSize { get => (double)GetValue(MinFontSizeProperty); set => SetValue(MinFontSizeProperty, value); } public int MaxLines { get => (int)GetValue(MaxLinesProperty); set => SetValue(MaxLinesProperty, value); } public AutoFitTextBlock() { Loaded += AutoFitTextBlock_Loaded; SizeChanged += (_, _) => AdjustFontSize(); } private void AutoFitTextBlock_Loaded(object sender, RoutedEventArgs e) { _baseFontSize = FontSize; WatchProperty(TextProperty); WatchProperty(FontSizeProperty); WatchProperty(FontFamilyProperty); WatchProperty(FontStyleProperty); WatchProperty(FontWeightProperty); WatchProperty(FontStretchProperty); WatchProperty(TextWrappingProperty); AdjustFontSize(); } private void WatchProperty(DependencyProperty dp) { var descriptor = DependencyPropertyDescriptor.FromProperty(dp, typeof(AutoFitTextBlock)); descriptor?.AddValueChanged(this, (_, _) => { if (!_isAdjusting && dp == FontSizeProperty) _baseFontSize = FontSize; AdjustFontSize(); }); } private static void OnAutoFitPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is AutoFitTextBlock tb) tb.AdjustFontSize(); } private void AdjustFontSize() { if (_isAdjusting || !IsLoaded) return; if (ActualWidth <= 0 || string.IsNullOrWhiteSpace(Text)) return; _isAdjusting = true; try { double startFont = _baseFontSize; double minFont = MinFontSize; FontSize = startFont; while (FontSize > minFont) { if (Fits(FontSize)) return; FontSize -= 0.5; } FontSize = minFont; } finally { _isAdjusting = false; } } private bool Fits(double fontSize) { double pixelsPerDip = VisualTreeHelper.GetDpi(this).PixelsPerDip; var formattedText = new FormattedText( Text ?? string.Empty, CultureInfo.CurrentUICulture, FlowDirection, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), fontSize, Foreground, pixelsPerDip) { MaxTextWidth = Math.Max(0, ActualWidth), Trimming = TextTrimming.None }; if (TextWrapping == TextWrapping.Wrap) formattedText.MaxLineCount = MaxLines; double lineHeight = fontSize * 1.25; double maxAllowedHeight = lineHeight * MaxLines; return formattedText.Height <= maxAllowedHeight + 1; } } }