XAML 仅自动调整wpf控件的字体大小

eqfvzcg8  于 2023-05-11  发布在  其他
关注(0)|答案(2)|浏览(181)

我有一个网格布局,看起来像这样:



这是它的代码:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="2.5*"/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition/>
        <RowDefinition Height="2.5*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="2.5*"/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition/>
        <ColumnDefinition Width="2.5*"/>
    </Grid.ColumnDefinitions>

    <TextBox x:Name="username" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Text="Username"
                    GotFocus="RemoveLabel" LostFocus="RestoreLabel"
                    HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
    <TextBox x:Name="password" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Text="Password"
                GotFocus="RemoveLabel" LostFocus="RestoreLabel"
                HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
    <Button x:Name="signUp" Grid.Row="5" Grid.Column="1" Content="Sign up"/>
    <Button x:Name="signIn" Grid.Row="5" Grid.Column="3" Content="Sign in"/>
</Grid>

此行为是所需的:

唯一缺少的是在屏幕上调整每个控件中文本的大小,以利用该控件中的可用空间。
我尝试使用Viewbox,但无论Stretch属性的值如何,它都会更改控件的大小,我不知道如何强制它根据网格保持大小。
我主要在这篇文章中寻找答案:How to automatically scale font size for a group of controls?

thtygnil

thtygnil1#

就我所能想象的来说,不会有一个放之四海而皆准的解决方案,但是这里有一个针对您的特定问题的解决方案,它还演示了两种不同的处理方法。

public static class AutoScale
{
    public static readonly DependencyProperty AutoscaleFontProperty = DependencyProperty.RegisterAttached(
        "AutoscaleFont",
        typeof(bool),
        typeof(AutoScale),
        new PropertyMetadata((sender, e) =>
        {
            if (!(sender is Control c))
                throw new NotSupportedException($"AutoscaleFont is for Control-derived classes only");
            if (e.NewValue == e.OldValue || !(e.NewValue is bool value))
                return;
            if (value)
                c.SizeChanged += OnSizeChangedRescaleFont;
            else
                c.SizeChanged -= OnSizeChangedRescaleFont;
        }));

    private static void OnSizeChangedRescaleFont(object sender, SizeChangedEventArgs e)
    {
        if (!(sender is Control c))
            throw new NotSupportedException($"AutoscaleFont is for Control-derived classes only");

        if (c is TextBox)
        {
            c.FontSize = c.ActualHeight * 0.8;
            return;
        }

        Border border = null;
        EnumVisual(c, fe =>
        {
            if (c is Button && fe is Border b)
            {
                border = b;
                return true;
            }
            return false;
        });
        if (border == null)
            return;

        if (!(border.Child is FrameworkElement child))
            return;

        double scale = 1;
        if (child.ActualWidth / child.ActualHeight > border.ActualWidth / border.ActualHeight)
        {
            // fit to width
            scale = border.ActualWidth / child.ActualWidth;
        }
        else
        {
            // fit to height
            scale = border.ActualHeight / child.ActualHeight;
        }

        child.RenderTransformOrigin = new Point(0.5, 0.5);
        child.RenderTransform = new ScaleTransform
        {
            ScaleX = scale,
            ScaleY = scale
        };
    }

    public static bool GetAutoscaleFont (DependencyObject obj)
    {
        return (bool)obj.GetValue(AutoscaleFontProperty);
    } 
    public static void SetAutoscaleFont(DependencyObject obj, bool value)
    {
        obj.SetValue(AutoscaleFontProperty, value);
    }

    private static void EnumVisual(FrameworkElement myVisual, Func<FrameworkElement, bool> action)
    {
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
        {
            // Retrieve child visual at specified index value.
            FrameworkElement child = VisualTreeHelper.GetChild(myVisual, i) as FrameworkElement;
            if (child == null) 
                continue;

            // Do processing of the child visual object.
            if (action != null)
            {
                if (action(child)) 
                    break;
            }

            // Enumerate children of the child visual object.
            EnumVisual(child, action);
        }
    }
}

要消费,只需说:

<TextBox x:Name="username"
             Grid.Row="1"
             Grid.Column="1"
             Grid.ColumnSpan="3"
             Text="Username"
             local:AutoScale.AutoscaleFont="True"
             HorizontalContentAlignment="Center"
             VerticalContentAlignment="Center" />

等等
这部分的内容是OnSizeChangedRescaleFont。对于任何特定的控件,执行此操作的方式都将依赖于控件。这是我认为缩放默认Button和默认TextBox字体的最佳方法。
你会注意到这些是完全不同的方法--对于TextBox,我只是将FontSize属性设置为实际高度的倍数,因为TextBox可以水平滚动,而且你可能不希望字体大小随着人们的输入而缩小。
对于内容是静态的Button,一旦找到Border及其子对象,就可以使用RenderTransform使其随着窗口大小的调整而缩放。这取决于内容的宽度与按钮的宽度。
当然,这远非完美,但希望它演示了概念,并包含您可以用来构建的代码。一个完全健壮的解决方案将涉及子类化控件,重写ArrangeOverride,并重新模板化它们。这实际上要复杂得多。不过,这应该满足您的字面示例。

1tu0hz3e

1tu0hz3e2#

有一个SizeChanged路由事件,顾名思义,每次控件更改其大小时都会触发该事件。因此,您可以添加一个路由事件处理程序(方法),用于侦听每次更改控件大小并更改其字体大小或任何属性。
下面是针对特定情况的实现:

private void ResizeFont(object sender, SizeChangedEventArgs e)
{
    var control = sender as Control;
    if (control == null) return;
    control.FontSize = control.ActualHeight / 2;
}

在xaml中,您只需要将SizeChanged属性添加到控件中:

<TextBox x:Name="username" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="3" Text="Username"
        GotFocus="RemoveLabel" LostFocus="RestoreLabel"
        HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
        SizeChanged="ResizeFont"/>
<TextBox x:Name="password" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Text="Password"
        GotFocus="RemoveLabel" LostFocus="RestoreLabel"
        HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
        SizeChanged="ResizeFont"/>
<Button x:Name="signUp" Grid.Row="5" Grid.Column="1" Content="Sign up" SizeChanged="ResizeFont"/>
<Button x:Name="signIn" Grid.Row="5" Grid.Column="3" Content="Sign in" SizeChanged="ResizeFont"/>

这对于宽高比的按钮和文本框很有效,它们只有一行文本(文本块会根据字体大小调整高度,因此不适用于它们)。如果你需要一些不同的行为,这个方法也很容易修改,例如当使用多行TextBox时,你可以根据ActualWidth而不是ActualHeight来更改字体大小。
This是特定情况下的结果。

相关问题