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

172
假设我想要一个TextBlock的宽度等于其父容器的宽度(即从一侧延伸到另一侧),或者是其父容器宽度的百分比,如何在XAML中实现这一目标而不指定绝对值?
我想这样做是为了当父容器扩展(增加其Width)时,其子元素也会自动扩展。(基本上就像在HTML和CSS中一样)

请查看这个答案 - Jesper Fyhr Knudsen
使用cwap的答案,并在网格中包含tb,将其对齐方式设置为stretch。 - Shimmy Weitzhandler
7个回答

260

你可以将文本框放在网格中,以对网格的行或列进行百分比值,并让文本框自动填充其父单元格(默认情况下会这样做)。例如:

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

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

这将使#1的宽度为总宽度的2/5,#2的宽度为总宽度的3/5。


7
我已经尝试过了,但是遇到了这个错误:'2*'字符串无法转换为长度。 - Andreas Grech
1
:-),... * 被称为“星号”。 - EightyOne Unite
6
实际上,*(星号)是小星星的意思 ;) 参考网站:http://www.etymonline.com/index.php?term=asterisk - Pratik Deoghare
7
即使TextBox在Grid中,它也不起作用。宽度可以设置为Double、Qualified Double(由双精度值和px、in、cm或pt组成),或者Auto。请参阅http://msdn.microsoft.com/en-GB/library/system.windows.frameworkelement.width.aspx。 - Darren
2
是的,这实际上是一个非常糟糕的回复,但基于所有的赞,我想它帮助了某个人(很久以前),这就是为什么我没有删除它的原因。 - cwap
显示剩余3条评论

104

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

 <Textbox HorizontalAlignment="Stretch" ...

这将使文本框元素水平拉伸并水平填满所有父级空间(实际上这取决于您使用的父级面板,但对大多数情况应该有效)。

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


67

通常情况下,您会使用适合您场景的内置布局控件(例如,如果您希望相对于父级进行缩放,则使用网格作为父级)。如果您想要在任意父元素中实现此功能,您可以创建一个 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>

这真的很酷,我该如何用代码实现(设置文本框的宽度)? - Jeremy
10
由于 parameter 被视为字符串,在具有不同小数分隔符的文化中,双精度转换可能无法按预期工作,建议考虑添加 CultureInfo.InvariantCulture 到双精度转换中。 - Flat Eric
在搜索这个主题时,我遇到了这个答案:https://dev59.com/WEXXs4cB2Jgan1znymik#59934565,它基本上做了相同的事情,但更进一步地实现了一个附加属性。 - Jogge

41

对于任何遇到类似错误的人:无法将“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>

为什么不使用<RowDefinition Height="auto" /> - Ben
2
"auto" 只占用控件所需的空间。 - Yama
非常感谢,这应该是最佳答案。 - Mafii
这绝对是最好的答案。 - Knut Valen

8

可以使用IValueConverter实现。从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>

4
我使用两种方法进行相对大小调整。我有一个名为Relative的类,其中包含三个附加属性ToWidthPercentHeightPercent。如果我想要一个元素是视觉树中任何元素的相对大小,并且感觉不像转换器方法那么奇怪,那么这个类就非常有用——尽管使用适合您的、您满意的方法。
另一种方法则更加巧妙。在您希望使用相对大小的位置添加一个ViewBox,然后在其中添加一个100宽度的Grid。然后,如果您在其中添加一个宽度为10的TextBlock,它显然是100的10%。 ViewBox将根据其所占空间缩放Grid,因此如果它是页面上唯一的元素,则Grid将被缩放到全宽,并且您的TextBlock实际上被缩放为页面的10%。
如果您没有在Grid上设置高度,则它将缩小以适应其内容,因此所有内容都将被相对调整。您必须确保内容不会变得太高,即不会改变给予ViewBox的空间的纵横比,否则它将开始缩放高度。您可以使用StretchUniformToFill解决这个问题。

4

我知道这不是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%),并且在需要时会拉伸。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接