“Automatic resizing of only the font of a wpf control” 只翻译此部分。

huangapple go评论53阅读模式
英文:

Automatic resizing of only the font of a wpf control

问题

我有一个类似这样的网格布局:

“Automatic resizing of only the font of a wpf control” 只翻译此部分。.

这是它的代码:

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

这个行为是期望的:

“Automatic resizing of only the font of a wpf control” 只翻译此部分。

唯一缺失的是屏幕调整大小时,每个控件中的文本也要随之调整大小,以利用该控件中的空闲空间。

我尝试使用Viewbox,但无论Stretch属性的值如何,它都会更改控件的大小,我不知道如何强制其保持根据网格的大小调整。我主要在这篇帖子中寻找答案:
https://stackoverflow.com/questions/15641473/how-to-automatically-scale-font-size-for-a-group-of-controls

英文:

I've got a grid layout that looks like this:

“Automatic resizing of only the font of a wpf control” 只翻译此部分。.

This is its code:

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

This behavior is desired:

“Automatic resizing of only the font of a wpf control” 只翻译此部分。

The only thing that is missing is on screen resize for the text in each control to also resize to utilize the free space in that control.

I tried using Viewbox, but regardless of the value of the Stretch property, it changes the size of the control and I don't know how to force it to remain sized according to the grid.

I primarily looked for answers on this post:
https://stackoverflow.com/questions/15641473/how-to-automatically-scale-font-size-for-a-group-of-controls

答案1

得分: 1

这是一段代码,它展示了如何根据窗口大小调整控件的字体大小,以适应不同的控件类型。这段代码包括一个名为 AutoScale 的静态类,以及两个附加属性 AutoscaleFont 和相应的 Get 和 Set 方法,用于控制字体大小的自动缩放。主要逻辑在 OnSizeChangedRescaleFont 方法中,它根据控件的类型和大小来调整字体大小。

以下是代码中的重要部分:

public static class AutoScale
{
    public static readonly DependencyProperty AutoscaleFontProperty = DependencyProperty.RegisterAttached(
        "AutoscaleFont",
        typeof(bool),
        typeof(AutoScale),
        new PropertyMetadata((sender, e) =>
        {
            // ...
        }));

    private static void OnSizeChangedRescaleFont(object sender, SizeChangedEventArgs e)
    {
        // ...
    }

    // ...
}

代码的目的是根据控件的大小自动调整字体大小,以适应窗口大小的变化。这是一个示例,用于演示如何处理不同类型的控件。你可以根据需要将 local:AutoScale.AutoscaleFont="True" 添加到要自动缩放字体的控件上。

请注意,这只是示例代码,实际使用时可能需要根据具体情况进行进一步定制。

英文:

There isn't going to be a one-size-fits-all solution to this as far as I'm able to imagine, but here is a solution to your specific problem that also demonstrates two different ways of handling this.

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

To consume, just say:

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

etc.

The meat of this is in OnSizeChangedRescaleFont. The way to do this for any particular control is going to be control dependent. This is what I think is the best way to scale the font for both a default Button and a default TextBox.

You'll note these are completely different methods - for TextBox I'd simply set the FontSize property to be a multiple of the actual height because the TextBox could horizontally scroll, and you probably don't want the font size to shrink as people type anyway.

For Button where the content is static, once you locate the Border and its child you can use a RenderTransform to make it scale as the window resizes. This does a best-fit depending on the width of the content vs. the width of the button.

Of course this is far from perfect but hopefully it demonstrates the concepts and contains code you can use to build on. A completely robust solution would involve subclassing your controls, overriding ArrangeOverride, and re-templating them. That is, indeed, much more complex. This should satisfy your literal example though.

答案2

得分: -1

以下是您要翻译的内容:

有一个名为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

是特定情况下的结果。
“Automatic resizing of only the font of a wpf control” 只翻译此部分。

英文:

There is a SizeChanged routed event that as the name implies is triggered every time a control changes it's size. So you can add a routed event handler(method) that listens for everytime the control's size is changed and changes its font size or any property for that matter.

Here's an implementation for the specific case:

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

In the xaml you just need to add the SizeChanged property to the controls:

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

This works nicely for buttons and text boxes with a wide aspect ratio that have a single line of text (text blocks adapt their height to the font size, so it doesn't work for them). If you need some different behavior this method is also easy to modify, for example when using a TextBox with multiple lines you can change the font size to be based on the ActualWidth instead of ActualHeight.

This is the result in the specific case.
“Automatic resizing of only the font of a wpf control” 只翻译此部分。

huangapple
  • 本文由 发表于 2023年5月7日 21:46:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/76194316.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定