WPF: установка ширины (и высоты) в процентах

150

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

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

Андреас Греч
источник
Посмотрите на этот ответ .
Джеспер Фир Кнудсен
Используйте ответ cwap и включите tb в сетку, чтобы выровнять его по растяжке.
Shimmy Weitzhandler

Ответы:

91

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

 <Textbox HorizontalAlignment="Stretch" ...

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

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

gcores
источник
222

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

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

cwap
источник
7
Я уже пробовал это, но я получаю эту ошибку: '2 *' строка не может быть преобразована в длину. '
Андреас Греч
6
На самом деле, * (звездочка) - маленькая звезда;) etymonline.com/index.php?term=asterisk
Pratik Deoghare
73
Моя мама говорит, что я звезда
Денис Вессельс
7
Это не работает, даже если TextBox находится в сетке. Ширина может быть установлена ​​на Double, Qualified Double (двойное значение, за которым следуют px, in, cm или pt) или Auto. См. Msdn.microsoft.com/en-GB/library/…
Даррен
2
Да .. Это на самом деле очень плохой ответ, но, основываясь на всех возражениях, я полагаю, что это помогло кому-то (давным-давно), поэтому я не удалил его.
cwap
59

Как правило, вы используете встроенный элемент управления макета, соответствующий вашему сценарию (например, используйте сетку в качестве родительского элемента, если вы хотите масштабировать относительно родительского элемента). Если вы хотите сделать это с произвольным родительским элементом, вы можете создать 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>
KVB
источник
Это действительно круто, как я могу сделать это в коде (установить ширину текстового поля)?
Джереми
9
Вы должны рассмотреть возможность добавления CultureInfo.InvariantCultureк двойному преобразованию, потому что parameterоно считается stringи в культурах с другим decimal separatorоно не будет работать, как ожидалось.
Квартира Эрика
33

Для тех, кто получает ошибку, например: «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" />?
Бен
2
«auto» занимает столько места, сколько нужно контролю
Yama
Большое вам спасибо, это должен быть главный ответ.
Мафия
Это определенно лучший ответ.
Кнут Вален
7

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

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

Crippeoblade
источник
4

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

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

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

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

Люк Пуплетт
источник