WPF: установка ширины (и высоты) как процентного значения

Скажем, я хочу, чтобы TextBlock имел Width равный Width родительского контейнера (т. Е. Растягивался из стороны в сторону) или процент от родительского контейнера Width, как я могу сделать это в XAML без указания абсолютных значений?

Я хочу сделать это так, чтобы, если контейнер родительского контейнера позже будет расширен (его Width увеличено), его дочерние элементы также будут расширены автоматически. (в основном, как в HTML и CSS)


person Andreas Grech    schedule 04.04.2009    source источник
comment
Взгляните на этот ответ.   -  person Jesper Fyhr Knudsen    schedule 04.04.2009
comment
Используйте ответ cwap и включите tb в настройку сетки, чтобы его выравнивание растянулось.   -  person Shimmy Weitzhandler    schedule 06.01.2010


Ответы (7)


Чтобы растянуть его до того же размера, что и родительский контейнер, можно использовать атрибут:

 <Textbox HorizontalAlignment="Stretch" ...

Это заставит элемент Textbox растянуться по горизонтали и заполнить все родительское пространство по горизонтали (на самом деле это зависит от родительской панели, которую вы используете, но должно работать в большинстве случаев).

Проценты можно использовать только со значениями ячеек сетки, поэтому другой вариант - создать сетку и поместить текстовое поле в одну из ячеек с соответствующим процентом.

person gcores    schedule 04.04.2009

Вы можете поместить текстовые поля в сетку, чтобы задать процентные значения в строках или столбцах сетки и позволить текстовым полям автоматически заполнять свои родительские ячейки (как они будут по умолчанию). Пример:

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

person cwap    schedule 04.04.2009
comment
Я уже пробовал это, но получаю такую ​​ошибку: строка «2 *» не может быть преобразована в длину ». - person Andreas Grech; 04.04.2009
comment
Ваш ответ мне очень помог, спасибо! Я пытался выяснить, что делает * в течение многих лет, вы так коротко и хорошо объяснили это. - person Shimmy Weitzhandler; 06.01.2010
comment
:-), ... * называется звездой - person EightyOne Unite; 18.11.2010
comment
На самом деле * (Asterisk) - это маленькая звездочка;) etymonline.com/index.php?term = звездочка - person Pratik Deoghare; 10.12.2010
comment
Моя мама говорит, что я звезда - person Denys Wessels; 21.01.2014
comment
Это не работает, даже если TextBox находится в сетке. Для ширины можно установить значение Double, Qualified Double (значение типа double, за которым следуют пиксели, дюймы, см или точки) или Auto. См. msdn.microsoft.com/en-GB/library. / - person Darren; 19.06.2014
comment
Да ... На самом деле это очень плохой ответ, но, судя по всем положительным голосам, я полагаю, что это помогло кому-то (это было давно), поэтому я не удалил его. - person cwap; 24.06.2014
comment
Теперь, когда мы находимся в конце 17-го, 2 * 3 * абсолютно верно. Даже графический интерфейс Visual Studio предоставляет поля для указания таких значений. - person Norman; 20.10.2017
comment
Просто хотел предложить еще одно подтверждение того, что это работает с новыми версиями xaml. - person prw56; 08.04.2020

Как правило, вы должны использовать встроенный элемент управления макетом, подходящий для вашего сценария (например, используйте сетку в качестве родительского элемента, если вы хотите масштабировать относительно родительского элемента). Если вы хотите сделать это с произвольным родительским элементом, вы можете создать 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>
person kvb    schedule 04.04.2009
comment
Это действительно круто, как это сделать в коде (установить ширину текстового поля)? - person Jeremy; 23.06.2011
comment
Вам следует подумать о добавлении CultureInfo.InvariantCulture к двойному преобразованию, потому что parameter рассматривается как string и в культурах с другими decimal separator он не будет работать должным образом. - person Flat Eric; 04.12.2014

Для тех, кто получает сообщение об ошибке: Строка '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>
person Zain Ali    schedule 17.09.2012
comment
Почему не <RowDefinition Height="auto" />? - person Ben; 30.08.2014
comment
auto занимает ровно столько места, сколько нужно контроллеру - person Yama; 18.03.2015
comment
Большое спасибо, это должен быть главный ответ. - person Mafii; 28.06.2016
comment
Это определенно лучший ответ. - person Knut Valen; 29.06.2016

Можно использовать реализацию 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>
person Adem Catamak    schedule 31.08.2016

Я использую два метода относительного размера. У меня есть класс под названием Relative с тремя присоединенными свойствами To, WidthPercent и HeightPercent, который полезен, если я хочу, чтобы элемент имел относительный размер элемента в любом месте визуального дерева и чувствовал себя менее взломанным, чем подход преобразователя - хотя используйте то, что работает для ты, которым ты доволен.

Другой подход более хитрый. Добавьте ViewBox там, где вы хотите указать относительные размеры внутри, а затем внутри него добавьте Grid с шириной 100. Затем, если вы добавите TextBlock с шириной 10 внутри него, это, очевидно, будет 10% от 100.

ViewBox будет масштабировать Grid в соответствии с предоставленным ему пространством, поэтому, если это единственное, что есть на странице, то Grid будет масштабироваться на всю ширину и эффективно, ваш TextBlock масштабируется до 10% страницы.

Если вы не установите высоту для Grid, он будет уменьшаться, чтобы соответствовать его содержимому, поэтому все будет иметь относительно размер. Вам нужно будет убедиться, что контент не становится слишком высоким, т.е. начинает изменять соотношение сторон пространства, предоставленного ViewBox, иначе он также начнет масштабировать высоту. Вероятно, вы можете обойти это, установив Stretch из UniformToFill.

person Luke Puplett    schedule 30.07.2013

Я знаю, что это не 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%) и при необходимости растягивается.

person Crippeoblade    schedule 05.04.2009