所以我正在尝试构建一个表单,它会根据父容器的可用宽度和相同的列百分比自动按比例上下缩放,如下所示:
周围的其他内容也需要缩放,例如图像和按钮(它们不会在同一个网格中),根据我目前所读的内容,使用 Viewbox 是可行的方法。
但是,当我用 Stretch="Uniform"将我的网格包裹在一个 Viewbox 中时,Textbox 控制每个折叠到它们的最小宽度,看起来像这样:
如果我增加容器宽度,一切都会按预期缩放(好),但文本框仍然折叠到它们的最小可能宽度(坏):
...但我不想要这种行为 - 我希望文本框元素宽度与网格列宽相关联,而不是依赖于内容。
现在,我查看了各种 SO 问题,这个问题最接近我所追求的问题: How to automatically scale font size for a group of controls?
...但它仍然没有真正专门处理文本框宽度行为(当它与 Viewbox 行为交互时),这似乎是主要问题。
我尝试了多种方法 - 不同的 HorizontalAlignment="Stretch"设置等等,但到目前为止都没有任何效果。这是我的 XAML:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="5" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<StackPanel.Background>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Silver" Offset="0"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Background>
<Viewbox Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="Field A" Grid.Column="0" Grid.Row="0" />
<TextBox Grid.Column="1" Grid.Row="0" HorizontalAlignment="Stretch"></TextBox>
<Label Content="Field B" Grid.Column="2" Grid.Row="0" />
<TextBox Grid.Column="3" Grid.Row="0" HorizontalAlignment="Stretch"></TextBox>
</Grid>
</Viewbox>
<Label Content="Other Stuff"/>
</StackPanel>
<GridSplitter Grid.Column="1" HorizontalAlignment="Stretch" Height="100" Width="5"/>
<StackPanel Grid.Column="2">
<Label Content="Body"/>
</StackPanel>
</Grid>
</Window>
最佳答案
此行为的原因是 Viewbox
子级被赋予了无限的空间来测量其所需的大小。将 TextBox
es 拉伸(stretch)到无限 Width
没有多大意义,因为无论如何都无法呈现,因此返回它们的默认大小。
您可以使用转换器来实现所需的效果。
public class ToWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double gridWidth = (double)value;
return gridWidth * 2/6;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
您可以将所有内容连接起来添加这些资源。
<Viewbox.Resources>
<local:ToWidthConverter x:Key="ToWidthConverter"/>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Width"
Value="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type Grid}},
Converter={StaticResource ToWidthConverter}}"/>
</Style>
</Viewbox.Resources>
更新
I'm having trouble understanding the original problem of the infinite grid width.
无限空间方法通常用于确定DesiredSize UIElement
的。简而言之,您为控件提供它可能需要的所有空间(无限制),然后对其进行测量以检索其所需的大小。
Viewbox
使用这种方法来测量它的 child ,但是我们的 Grid
是动态大小的(没有 Height
或 Width
是在代码中设置),因此 Viewbox
在子网格处下降到另一个级别,以查看它是否可以通过获取组件的总和来确定大小。
但是,当组件总和超过可用总大小时,您可能会遇到问题,如下所示。
我用标签 Foo
和 Bar
替换了文本框,并将它们的背景颜色设置为灰色。现在我们可以看到 Bar
正在入侵 Body
领域,这显然不是我们想要发生的事情。
同样,问题的根源来自 Viewbox
不知道如何将无穷大分成 6 等份(映射到列宽 1*
、2 *
, 1*
,2*
), 所以我们需要做的就是用网格宽度恢复链接。在 ToWidthConverter
中,目的是将 TextBox
的 Width
映射到 Grid
的 ColumnWidth
2*
,所以我使用了 gridWidth * 2/6
。现在 Viewbox
可以再次求解方程:每个 TextBox
得到 gridwidth
的三分之一,每个 Label
得到一个一半(1*
对比 2*
)。
当然,当您通过引入新列来打乱事物时,您必须注意使组件的总和与总可用宽度保持同步。换句话说,方程需要是可解的。 输入数学、所需大小(您未约束的控件的总和,在我们的示例中为标签)和转换后的大小(作为 gridWidth
的一部分,在我们的示例中为文本框)需要小于或等于可用大小(在我们的示例中为 gridWidth
)。
我发现如果您对 TextBox
使用转换后的大小,并让星形大小的 ColumnWidth
处理大多数其他大小,则缩放效果很好。请记住保持在总可用大小之内。
增加一些灵 active 的一种方法是将 ConverterParameter
添加到组合中。
public class PercentageToWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double gridWidth = (double)value;
double percentage = ParsePercentage(parameter);
return gridWidth * percentage;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
private double ParsePercentage(object parameter)
{
// I chose to let it fail if parameter isn't in right format
string[] s = ((string)parameter).Split('/');
double percentage = Double.Parse(s[0]) / Double.Parse(s[1]);
return percentage;
}
}
将 gridWidth
平均分成 10 份,并将这些份额相应地分配给组件的示例。
<Viewbox Stretch="Uniform">
<Viewbox.Resources>
<local:PercentageToWidthConverter x:Key="PercentageToWidthConverter"/>
</Viewbox.Resources>
<Grid Background="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Label Content="Field A" Grid.Column="0" />
<TextBox Grid.Column="1"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type Grid}},
Converter={StaticResource PercentageToWidthConverter},
ConverterParameter=2/10}" />
<Label Content="Field B" Grid.Column="2" />
<TextBox Grid.Column="3"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type Grid}},
Converter={StaticResource PercentageToWidthConverter},
ConverterParameter=3/10}" />
<Button Content="Ok" Grid.Column="4"
Width="{Binding ActualWidth,
RelativeSource={RelativeSource AncestorType={x:Type Grid}},
Converter={StaticResource PercentageToWidthConverter},
ConverterParameter=1/10}" />
</Grid>
</Viewbox>
注意每个控件的份额,分组为 2 - 2 - 2 - 3 - 1(按钮宽度为 1)。
最后,根据您所追求的可重用性,还有其他一些方法可以处理此问题:
- 在您的根
Grid
上设置固定大小。缺点:- 每次更改组件时都需要进行微调(以实现 所需的水平/垂直/字体大小比例)
- 这个比率可能会因不同的主题、Windows 版本、......
- 添加一个
行为
。正如您在链接的FontSize
帖子中的一个答案中所做的那样,而是实现了将列宽映射到gridWidth
的部分。 - 按照@Grx70 的建议创建自定义面板。
关于c# - 使用 Viewbox 缩放包含标签和文本框的网格,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50457409/