Примеры компоновки

80

Колонка настроек

Контейнеры компоновки, подобные Grid, значительно упрощают задачу создания общей структуры окна. Например, рассмотрим окно с настройками. Это окно располагает свои индивидуальные компоненты — метки, текстовые поля и кнопки — в табличной структуре.

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

Далее необходимо создать колонки. Размер первой и последней колонки определяется так, чтобы уместить их содержимое (текст метки и кнопку Browse соответственно). Средняя колонка получает все оставшееся пространство, а это значит, что она будет расти при увеличении размера окна, предоставляя больше места, чтобы видеть выбранную папку. (Если хотите ограничить ее ширину, можете указать свойство MaxWidth при определении колонки, как это делается с индивидуальными элементами.)

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Label Padding="5" Margin="5">Home:</Label>
        <Label Grid.Row="1" Padding="5" Margin="5">Network:</Label>
        <Label Grid.Row="2" Padding="5" Margin="5">Web:</Label>
        <Label Grid.Row="3" Padding="5" Margin="5">Secondary:</Label>
        <TextBox Grid.Row="0" Grid.Column="1" Margin="3" Height="auto"
                 VerticalAlignment="Center">C:\</TextBox>
        <TextBox Grid.Row="1" Grid.Column="1" Margin="3" Height="auto"
                 VerticalAlignment="Center">e:\Shared</TextBox>
        <TextBox Grid.Row="2" Grid.Column="1" Margin="3" Height="auto"
                 VerticalAlignment="Center">C:\Web</TextBox>
        <TextBox Grid.Row="3" Grid.Column="1" Margin="3" Height="auto"
                 VerticalAlignment="Center">C:\</TextBox>
        <Button Grid.Row="0" Grid.Column="2" Padding="3" Margin="4">Обзор</Button>
        <Button Grid.Row="1" Grid.Column="2" Padding="3" Margin="4">Обзор</Button>
        <Button Grid.Row="2" Grid.Column="2" Padding="3" Margin="4">Обзор</Button>
        <Button Grid.Row="3" Grid.Column="2" Padding="3" Margin="4">Обзор</Button>
    </Grid>
Колонка настроек

Контейнер Grid требует некоторого минимального пространства — достаточного, чтобы уместить полный текст метки, кнопку просмотра и несколько пикселей в средней колонке, отобразив текстовое поле. Если установить размеры включающего окна меньше этих, то некоторое содержимое будет усечено. Как всегда, чтобы предотвратить такую ситуацию, имеет смысл использовать свойства окна MinWidth и MinHeight.

При наличии базовой структуры остается просто разместить элементы в правильных ячейках. Однако также потребуется тщательно продумать поля и выравнивание. Каждый элемент нуждается в базовом поле (подходящим значением для него будет 3 единицы), чтобы создать небольшой отступ от края окна. Вдобавок метка и текстовое поле должны быть центрированы по вертикали, потому что их высота меньше, чем у кнопки Обзор. И, наконец, текстовое поле должно использовать режим автоматической установки размера, растягиваясь для того, чтобы уместить всю колонку.

Один факт, который не сразу очевиден, связан с тем, насколько гибким является это окно благодаря использованию элемента управления Grid. Ни один из индивидуальных элементов — метки, текстовые поля и кнопки — не имеют жестко закодированных позиций и размеров. В результате сетку легко модифицировать, просто изменяя элементы ColumnDefinition. Более того, если вы добавите строку, которая имеет более длинный текст метки (что потребует расширения первой колонки), вся сетка будет откорректирована автоматически, сохраняя согласованность, в том числе и для добавленных строк.

Если понадобится добавить элементы между существующими строками, такие как разделительные линии между разными частями окна, можно сохранить те же колонки, но использовать свойство ColumnSpan для растяжения единственного элемента на большую область.

Динамическое содержимое

Как демонстрирует показанная выше колонка настроек, окна, использующие контейнеры компоновки WPF, легко поддаются изменениям и адаптации по мере развития приложения. И преимущество этой гибкости проявляется не только во время проектирования. Это также ценное приобретение, если нужно отобразить содержимое, изменяющееся динамически.

Примером может служить локализованный текст — текст, который отображается в пользовательском интерфейсе и нуждается в переводе на разные языки для разных географических регионов. В приложениях старого стиля, опирающихся на координатные системы, изменение текста может разрушить внешний вид окна — в частности, потому, что краткие предложения английского языка становятся существенно длиннее на многих других языках. Даже если элементам позволено изменять свои размеры, чтобы вместить больший текст, это может нарушить общий баланс окна.

Ниже показано, как можно избежать этих неприятностей, если разумно применять контейнеры компоновки WPF. В этом примере пользовательский интерфейс имеет опции краткого и длинного текста. Когда используется длинный текст, кнопки, содержащие текст, изменяют свой размер автоматически, расталкивая соседнее содержимое. И поскольку кнопки измененного размера разделяют один и тот же контейнер компоновки (в данном случае — колонку таблицы), весь раздел пользовательского интерфейса изменяет свой размер. В результате получается, что кнопки сохраняют согласованный размер — размер самой большой из них.

Чтобы заставить это работать, окно оснащено таблицей из двух колонок и двух строк. Колонка слева принимает кнопки изменяемого размера, а колонка справа — текстовое поле. Нижняя строка используется для кнопки Close (Закрыть). Она находится в той же таблице, поэтому изменяет свой размер вместе с верхней строкой:

<Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"></RowDefinition>
            <RowDefinition Height="auto"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Button Name="Btn_next" Margin="5,5,5,0" Padding="3" MinWidth="80">&gt; Next</Button>
            <Button Name="Btn_prev" Margin="5" Padding="3" MinWidth="80">Prev &lt;</Button>   
            <CheckBox Margin="5,0,5,0" Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked">Растянуть</CheckBox>
        </StackPanel>
        <TextBox Grid.Column="1" Margin="0,5,5,5" BorderBrush="LightBlue" BorderThickness="2"
                 Padding="5" TextWrapping="WrapWithOverflow">
            Windows Presentation Foundation (WPF) — это набор инструментов для построения пользовательских интерфейсов, 
            появившийся в версии .NET 3.0. Основная цель WPF состоит в интеграции и унификации множества ранее 
            разрозненных настольных технологий (двух- и трехмерная графика, разработка окон и элементов управления и т.п.) 
            в единую унифицированную программную модель.
        </TextBox>
        <Button Grid.Row="1" Padding="3" Margin="5,0,5,5">Cancel</Button>
    </Grid>
namespace WpfApplication1
{
    /// <summary>
    /// Логика взаимодействия для MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void CheckBox_Checked(object sender, RoutedEventArgs e)
        {
            Btn_next.Content = "--> Next other page and window";
            Btn_prev.Content = "<-- Prev other page and window";
        }

        private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
        {
            Btn_next.Content = "> Next";
            Btn_prev.Content = "< Prev";
        }
    }
}
Динамическое содержимое
Автоматический ресайз текстового поля
Пройди тесты
Лучший чат для C# программистов