diff --git a/Ink Canvas/Helpers/AutoFontSizeHelper.cs b/Ink Canvas/Helpers/AutoFontSizeHelper.cs
index 1ae9464f..c14b74a8 100644
--- a/Ink Canvas/Helpers/AutoFontSizeHelper.cs
+++ b/Ink Canvas/Helpers/AutoFontSizeHelper.cs
@@ -5,22 +5,13 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
-using System.Windows.Markup;
-using System.Windows.Media.TextFormatting;
-using System.Windows.Media.Imaging;
-using System.Windows.Documents;
-using System.Windows.Input;
-using System.Windows.Shapes;
-using System.Windows.Data;
-using System.Windows.Ink;
-using System.Windows.Interop;
-using System.Windows.Media.Animation;
-using System.Windows.Navigation;
namespace Ink_Canvas.Helpers
{
///
- /// 让 TextBlock 在可用宽度不足时自动缩小字号(只缩小不放大),用于避免英文等长文本被截断。
+ /// Automatically shrinks text to fit available width.
+ /// Supports TextBlock and Label.
+ /// Only shrinks, never enlarges above MaxFontSize.
///
public static class AutoFontSizeHelper
{
@@ -76,170 +67,247 @@ namespace Ink_Canvas.Helpers
private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- var tb = d as TextBlock;
- if (tb == null) return;
+ if (!(d is FrameworkElement fe)) return;
+ if (!(fe is TextBlock) && !(fe is Label)) return;
if ((bool)e.NewValue)
{
- tb.SizeChanged += TextBlock_OnSizeChanged;
- tb.Loaded += TextBlock_OnLoaded;
- tb.Unloaded += TextBlock_OnUnloaded;
+ fe.SizeChanged += Element_OnSizeChanged;
+ fe.Loaded += Element_OnLoaded;
+ fe.Unloaded += Element_OnUnloaded;
+ TryHookContentChanged(fe, true);
- try
- {
- var dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
- dpd?.AddValueChanged(tb, TextBlock_OnTextChanged);
- }
- catch
- {
- // 忽略:极端情况下 descriptor 可能不可用
- }
-
- // 让第一次布局完成后再做一次调整(避免 ActualWidth=0)
- tb.Dispatcher.BeginInvoke(new Action(() => TryAdjust(tb)), DispatcherPriority.Loaded);
+ fe.Dispatcher.BeginInvoke(new Action(() => TryAdjust(fe)), DispatcherPriority.Loaded);
}
else
{
- tb.SizeChanged -= TextBlock_OnSizeChanged;
- tb.Loaded -= TextBlock_OnLoaded;
- tb.Unloaded -= TextBlock_OnUnloaded;
-
- try
- {
- var dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
- dpd?.RemoveValueChanged(tb, TextBlock_OnTextChanged);
- }
- catch
- {
- }
+ fe.SizeChanged -= Element_OnSizeChanged;
+ fe.Loaded -= Element_OnLoaded;
+ fe.Unloaded -= Element_OnUnloaded;
+ TryHookContentChanged(fe, false);
}
}
private static void OnSizingPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
- if (d is TextBlock tb && GetIsEnabled(tb))
+ if (d is FrameworkElement fe && GetIsEnabled(fe))
{
- tb.Dispatcher.BeginInvoke(new Action(() => TryAdjust(tb)), DispatcherPriority.Loaded);
+ fe.Dispatcher.BeginInvoke(new Action(() => TryAdjust(fe)), DispatcherPriority.Loaded);
}
}
- private static void TextBlock_OnLoaded(object sender, RoutedEventArgs e)
+ private static void Element_OnLoaded(object sender, RoutedEventArgs e)
{
- if (sender is TextBlock tb) tb.Dispatcher.BeginInvoke(new Action(() => TryAdjust(tb)), DispatcherPriority.Loaded);
- }
-
- private static void TextBlock_OnUnloaded(object sender, RoutedEventArgs e)
- {
- // 这里不做额外处理;事件解绑由 IsEnabled 关闭或对象销毁处理
- }
-
- private static void TextBlock_OnSizeChanged(object sender, SizeChangedEventArgs e)
- {
- if (sender is TextBlock tb) TryAdjust(tb);
- }
-
- private static void TextBlock_OnTextChanged(object sender, EventArgs e)
- {
- if (sender is TextBlock tb) tb.Dispatcher.BeginInvoke(new Action(() => TryAdjust(tb)), DispatcherPriority.Loaded);
- }
-
- private static void TryAdjust(TextBlock tb)
- {
- if (tb == null) return;
- if (!GetIsEnabled(tb)) return;
- if (GetIsAdjusting(tb)) return;
-
- // 没有可用宽度时跳过
- var availableWidth = tb.ActualWidth;
- if (double.IsNaN(availableWidth) || availableWidth <= 1) return;
-
- // 文本为空时不需要调整
- var text = tb.Text;
- if (string.IsNullOrEmpty(text)) return;
-
- var min = GetMinFontSize(tb);
- if (double.IsNaN(min) || min <= 0) min = 6d;
-
- var step = GetStep(tb);
- if (double.IsNaN(step) || step <= 0.01) step = 0.5d;
-
- var max = GetMaxFontSize(tb);
- if (double.IsNaN(max) || max <= 0)
+ if (sender is FrameworkElement fe)
{
- max = tb.FontSize;
+ fe.Dispatcher.BeginInvoke(new Action(() => TryAdjust(fe)), DispatcherPriority.Loaded);
}
+ }
- if (double.IsNaN(max) || max <= 0) return;
+ private static void Element_OnUnloaded(object sender, RoutedEventArgs e)
+ {
+ // No extra cleanup required here.
+ }
- // 只做“缩小不放大”
- var startFont = Math.Min(tb.FontSize, max);
- if (startFont < min) startFont = min;
+ private static void Element_OnSizeChanged(object sender, SizeChangedEventArgs e)
+ {
+ if (sender is FrameworkElement fe) TryAdjust(fe);
+ }
- SetIsAdjusting(tb, true);
+ private static void Element_OnTextChanged(object sender, EventArgs e)
+ {
+ if (sender is FrameworkElement fe)
+ {
+ fe.Dispatcher.BeginInvoke(new Action(() => TryAdjust(fe)), DispatcherPriority.Loaded);
+ }
+ }
+
+ private static void TryHookContentChanged(FrameworkElement fe, bool add)
+ {
try
{
- // 如果当前已合适,直接回到 max(但不超过原本 fontSize),避免之前缩小后再变短不恢复
- // 注意:恢复也只在不超过 max 的范围内
- var desiredAtMax = MeasureTextWidth(tb, text, max);
+ DependencyPropertyDescriptor dpd = null;
+ if (fe is TextBlock)
+ {
+ dpd = DependencyPropertyDescriptor.FromProperty(TextBlock.TextProperty, typeof(TextBlock));
+ }
+ else if (fe is Label)
+ {
+ dpd = DependencyPropertyDescriptor.FromProperty(ContentControl.ContentProperty, typeof(ContentControl));
+ }
+
+ if (dpd == null) return;
+ if (add) dpd.AddValueChanged(fe, Element_OnTextChanged);
+ else dpd.RemoveValueChanged(fe, Element_OnTextChanged);
+ }
+ catch
+ {
+ // Ignore descriptor issues in rare runtime cases.
+ }
+ }
+
+ private static void TryAdjust(FrameworkElement fe)
+ {
+ if (fe == null) return;
+ if (!GetIsEnabled(fe)) return;
+ if (GetIsAdjusting(fe)) return;
+
+ var text = GetElementText(fe);
+ if (string.IsNullOrEmpty(text)) return;
+
+ var availableWidth = GetAvailableWidth(fe);
+ if (double.IsNaN(availableWidth) || availableWidth <= 1) return;
+
+ var min = GetMinFontSize(fe);
+ if (double.IsNaN(min) || min <= 0) min = 6d;
+
+ var step = GetStep(fe);
+ if (double.IsNaN(step) || step < 0.1) step = 0.5d;
+
+ var current = GetElementFontSize(fe);
+ if (double.IsNaN(current) || current <= 0) return;
+
+ var max = GetMaxFontSize(fe);
+ if (double.IsNaN(max) || max <= 0) max = current;
+
+ var startFont = Math.Min(current, max);
+ if (startFont < min) startFont = min;
+
+ SetIsAdjusting(fe, true);
+ try
+ {
+ var desiredAtMax = MeasureTextWidth(fe, text, max);
if (desiredAtMax > 0 && desiredAtMax <= availableWidth + 0.5)
{
- if (tb.FontSize != max) tb.FontSize = max;
+ if (Math.Abs(current - max) > 0.01) SetElementFontSize(fe, max);
return;
}
- double font = startFont;
- double desired = MeasureTextWidth(tb, text, font);
+ var font = startFont;
+ var desired = MeasureTextWidth(fe, text, font);
if (desired <= 0) return;
- // 逐步减小直到适配或触底
while (font > min && desired > availableWidth + 0.5)
{
font = Math.Max(min, font - step);
- desired = MeasureTextWidth(tb, text, font);
+ desired = MeasureTextWidth(fe, text, font);
if (desired <= 0) break;
}
- if (!double.IsNaN(font) && font > 0 && Math.Abs(tb.FontSize - font) > 0.01)
+ if (!double.IsNaN(font) && font > 0 && Math.Abs(current - font) > 0.01)
{
- tb.FontSize = font;
+ SetElementFontSize(fe, font);
}
}
finally
{
- SetIsAdjusting(tb, false);
+ SetIsAdjusting(fe, false);
}
}
- private static double MeasureTextWidth(TextBlock tb, string text, double fontSize)
+ private static string GetElementText(FrameworkElement fe)
+ {
+ if (fe is TextBlock tb) return tb.Text;
+ if (fe is Label label) return label.Content as string ?? label.Content?.ToString();
+ return null;
+ }
+
+ private static double GetElementFontSize(FrameworkElement fe)
+ {
+ if (fe is TextBlock tb) return tb.FontSize;
+ if (fe is Label label) return label.FontSize;
+ return double.NaN;
+ }
+
+ private static void SetElementFontSize(FrameworkElement fe, double value)
+ {
+ if (fe is TextBlock tb) tb.FontSize = value;
+ else if (fe is Label label) label.FontSize = value;
+ }
+
+ private static double GetAvailableWidth(FrameworkElement fe)
+ {
+ double width = double.PositiveInfinity;
+
+ if (fe.ActualWidth > 1) width = Math.Min(width, fe.ActualWidth);
+
+ if (fe.Parent is FrameworkElement parent && parent.ActualWidth > 1)
+ {
+ var parentWidth = parent.ActualWidth - fe.Margin.Left - fe.Margin.Right;
+ if (parentWidth > 1) width = Math.Min(width, parentWidth);
+ }
+
+ if (double.IsInfinity(width) || double.IsNaN(width) || width <= 1) return -1;
+
+ // Keep width as inner text area.
+ if (fe is Control control)
+ {
+ width -= control.Padding.Left + control.Padding.Right;
+ width -= control.BorderThickness.Left + control.BorderThickness.Right;
+ }
+ else if (fe is Border border)
+ {
+ width -= border.Padding.Left + border.Padding.Right;
+ width -= border.BorderThickness.Left + border.BorderThickness.Right;
+ }
+
+ return width;
+ }
+
+ private static double MeasureTextWidth(FrameworkElement fe, string text, double fontSize)
{
try
{
- var dpi = VisualTreeHelper.GetDpi(tb);
+ var dpi = VisualTreeHelper.GetDpi(fe);
var culture = CultureInfo.CurrentUICulture;
- // 使用 TextBlock 自身的语言/流向
- if (tb.Language != null)
+ if (fe.Language != null)
{
try
{
- culture = tb.Language.GetEquivalentCulture();
+ culture = fe.Language.GetEquivalentCulture();
}
catch
{
}
}
- var typeface = new Typeface(tb.FontFamily, tb.FontStyle, tb.FontWeight, tb.FontStretch);
+ var fontFamily = SystemFonts.MessageFontFamily;
+ var fontStyle = FontStyles.Normal;
+ var fontWeight = FontWeights.Normal;
+ var fontStretch = FontStretches.Normal;
+ Brush foreground = Brushes.Black;
+ var flowDirection = FlowDirection.LeftToRight;
+
+ if (fe is TextBlock tb)
+ {
+ fontFamily = tb.FontFamily;
+ fontStyle = tb.FontStyle;
+ fontWeight = tb.FontWeight;
+ fontStretch = tb.FontStretch;
+ foreground = tb.Foreground;
+ flowDirection = tb.FlowDirection;
+ }
+ else if (fe is Label label)
+ {
+ fontFamily = label.FontFamily;
+ fontStyle = label.FontStyle;
+ fontWeight = label.FontWeight;
+ fontStretch = label.FontStretch;
+ foreground = label.Foreground;
+ flowDirection = label.FlowDirection;
+ }
+
+ var typeface = new Typeface(fontFamily, fontStyle, fontWeight, fontStretch);
var formatted = new FormattedText(
text,
culture,
- tb.FlowDirection,
+ flowDirection,
typeface,
fontSize,
- tb.Foreground,
+ foreground,
dpi.PixelsPerDip);
- // 这里用包含尾随空白的宽度更接近实际布局
return formatted.WidthIncludingTrailingWhitespace;
}
catch
@@ -249,4 +317,3 @@ namespace Ink_Canvas.Helpers
}
}
}
-
diff --git a/Ink Canvas/MainWindow.xaml b/Ink Canvas/MainWindow.xaml
index 516b44f9..79f6df8c 100644
--- a/Ink Canvas/MainWindow.xaml
+++ b/Ink Canvas/MainWindow.xaml
@@ -71,9 +71,26 @@
-
+
-
+
+
+
+
+
+
@@ -6840,6 +6857,9 @@
+
+
+
+ Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
@@ -8403,7 +8425,8 @@
+ FontWeight="Bold" FontSize="8" Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
@@ -9228,7 +9251,8 @@
+ FontSize="8" Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ FontSize="8" Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ FontSize="8" Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ FontSize="8" Margin="0,1,0,0" TextAlignment="Center"
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
@@ -9573,7 +9600,7 @@
Opacity="{Binding ElementName=BtnUndo, Path=IsEnabled, Converter={StaticResource IsEnabledToOpacityConverter}}"
Foreground="{DynamicResource FloatBarForeground}" FontSize="8" Margin="0,1,0,0"
TextAlignment="Center"
- Style="{StaticResource AutoFitFloatBarLabel8}" />
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
@@ -9790,7 +9817,7 @@
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+ Style="{StaticResource AutoFitMainToolbarLabel8}" />
+
+
+