WPF: 将宽度(和高度)设置为百分比值

假设我希望一个 TextBlockWidth等于它的父容器的 Width(即,从一侧拉伸到另一侧)或者它的父容器 Width的一个百分比,那么我如何在不指定绝对值的情况下在 XAML中实现这一点呢?

我希望这样做,以便如果父容器容器稍后被展开(其’Width增加) ,其’子元素也将自动展开。(基本上,就像 HTML 和 CSS)

366135 次浏览

您可以将文本框放在网格中,对网格的行或列执行百分比值,并让文本框自动填充到它们的父单元格(默认情况下也是如此)。例如:

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>


<TextBox Grid.Column="0" />
<TextBox Grid.Column="1" />
</Grid>

这将使 # 12/5的宽度,和 # 23/5。

将其拉伸到与父容器相同大小的方法是使用属性:

 <Textbox HorizontalAlignment="Stretch" ...

这将使 Textbox 元素水平拉伸并水平填充所有父空间(实际上它取决于您正在使用的父面板,但在大多数情况下应该可以工作)。

百分比只能与网格单元格值一起使用,因此另一种选择是创建一个网格,并将文本框放在具有适当百分比的单元格中。

通常,您会使用适合您的场景的内置布局控件(例如,如果您希望相对于父级进行伸缩,则使用网格作为父级)。如果希望使用任意的父元素执行此操作,可以创建 ValueConverter 执行此操作,但它可能不会像您希望的那样干净。然而,如果你真的需要它,你可以这样做:

public class PercentageConverter : IValueConverter
{
public object Convert(object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
return System.Convert.ToDouble(value) *
System.Convert.ToDouble(parameter);
}


public object ConvertBack(object value,
Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

可以这样使用,得到一个子文本框的父画布宽度的10% :

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<local:PercentageConverter x:Key="PercentageConverter"/>
</Window.Resources>
<Canvas x:Name="canvas">
<TextBlock Text="Hello"
Background="Red"
Width="{Binding
Converter={StaticResource PercentageConverter},
ElementName=canvas,
Path=ActualWidth,
ConverterParameter=0.1}"/>
</Canvas>
</Window>

我知道它不是 XAML,但我对文本框的 SizeChanged 事件做了同样的处理:

private void TextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
{
TextBlock textBlock = sender as TextBlock;
FrameworkElement element = textBlock.Parent as FrameworkElement;
textBlock.Margin = new Thickness(0, 0, (element.ActualWidth / 100) * 20, 0);
}

文本框的大小似乎是其父文本框的80% (右侧边距为20%) ,并在需要时伸展。

对于任何出现以下错误的人: “2 *”字符串不能转换为“长度”。

<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" /><!--This will make any control in this column of grid take 2/5 of total width-->
<ColumnDefinition Width="3*" /><!--This will make any control in this column of grid take 3/5 of total width-->
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition MinHeight="30" />
</Grid.RowDefinitions>


<TextBlock Grid.Column="0" Grid.Row="0">Your text block a:</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="0">Your text block b:</TextBlock>
</Grid>

我使用两种方法进行相对大小调整。我有一个名为 Relative的类,它有三个附加属性: ToWidthPercentHeightPercent。如果我想让一个元素在可视化树中的任何地方都是一个元素的相对大小,并且感觉没有转换器方法那么糟糕,那么这个类非常有用——尽管使用适合你的方法,你也会感到满意。

另一种方法更为狡猾。在需要内部相对大小的地方添加一个 ViewBox,然后在内部添加一个宽度为100的 Grid。然后,如果你添加一个宽度为10的 TextBlock,它显然是100的10% 。

ViewBox将缩放的 Grid根据任何空间已经给予,所以如果它的唯一的东西在页面上,然后 Grid将被缩放全宽和有效地,你的 TextBlock被缩放到10% 的页面。

如果你没有在 Grid上设置一个高度,那么它就会缩小以适应它的内容,所以它的大小都是相对的。您必须确保内容不会变得太高,即开始改变给予 ViewBox的空间的长宽比,否则它也会开始缩放高度。你也许可以用 UniformToFillStretch来解决这个问题。

可以使用 IValueConverter 实现。Converter 类从 IValueConverter 继承一些参数,如 value(百分比)和 parameter(父级宽度) ,并返回所需的宽度值。在 XAML 文件中,组件的宽度设置为所需的值:

public class SizePercentageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
return 0.7 * value.ToDouble();


string[] split = parameter.ToString().Split('.');
double parameterDouble = split[0].ToDouble() + split[1].ToDouble() / (Math.Pow(10, split[1].Length));
return value.ToDouble() * parameterDouble;
}


public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Don't need to implement this
return null;
}
}

XAML:

<UserControl.Resources>
<m:SizePercentageConverter x:Key="PercentageConverter" />
</UserControl.Resources>


<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Width="{Binding Converter={StaticResource PercentageConverter}, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Border}},Path=ActualWidth}"
Height="{Binding Converter={StaticResource PercentageConverter}, ConverterParameter=0.6, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Border}},Path=ActualHeight}">
....
</ScrollViewer>