WPF: Сетка с полем столбец / строка / отступы? - PullRequest
118 голосов
/ 24 августа 2009

Можно ли легко указать поле и / или отступ для строк или столбцов в сетке WPF?

Конечно, я мог бы добавить дополнительные столбцы для разметки, но это похоже на работу с отступами / полями (это даст намного более простой XAML). Кто-нибудь извлек из стандартной Grid эту функциональность?

Ответы [ 16 ]

72 голосов
/ 24 августа 2009

RowDefinition и ColumnDefinition имеют тип ContentElement, а Margin является строго FrameworkElement свойством. Итак, на ваш вопрос, «это легко возможно?» ответ - это однозначно нет. И нет, я не видел ни одного макета панелей, демонстрирующих такую ​​функциональность.

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

33 голосов
/ 14 декабря 2012

Используйте элемент управления Border вне элемента управления ячейки и определите для него отступ:

    <Grid>
        <Grid.Resources >
            <Style TargetType="Border" >
                <Setter Property="Padding" Value="5,5,5,5" />
            </Style>
        </Grid.Resources>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <Border Grid.Row="0" Grid.Column="0">
            <YourGridControls/>
        </Border>
        <Border Grid.Row="1" Grid.Column="0">
            <YourGridControls/>
        </Border>

    </Grid>


Источник:

18 голосов
/ 14 апреля 2011

Вы можете использовать что-то вроде этого:

<Style TargetType="{x:Type DataGridCell}">
  <Setter Property="Padding" Value="4" />
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type DataGridCell}">
        <Border Padding="{TemplateBinding Padding}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
          <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>

Или, если вам не нужны привязки шаблона:

<Style TargetType="{x:Type DataGridCell}">
   <Setter Property="Template">
      <Setter.Value>
          <ControlTemplate TargetType="{x:Type DataGridCell}">
              <Border Padding="4">
                  <ContentPresenter />
              </Border>
          </ControlTemplate>
      </Setter.Value>
  </Setter>
</Style>
17 голосов
/ 28 августа 2009

Вы можете написать свой собственный класс GridWithMargin, унаследованный от Grid, и переопределить метод ArrangeOverride, чтобы применить поля

6 голосов
/ 16 февраля 2016

Думаю, я бы добавил свое собственное решение, потому что никто еще не упомянул об этом. Вместо того, чтобы создавать UserControl на основе Grid, вы можете настроить элементы управления, содержащиеся в grid, с помощью объявления стиля. Позаботится о добавлении отступов / полей ко всем элементам без необходимости их определения для каждого, что является громоздким и трудоемким. Например, если ваша сетка содержит только TextBlocks, вы можете сделать это:

<Style TargetType="{x:Type TextBlock}">
    <Setter Property="Margin" Value="10"/>
</Style>

Что похоже на "заполнение ячейки".

3 голосов
/ 14 февраля 2015

Недавно я столкнулся с этой проблемой при разработке программного обеспечения, и мне пришло в голову спросить ПОЧЕМУ? Почему они сделали это ... ответ был прямо передо мной. Строка данных является объектом, поэтому, если мы сохраняем ориентацию объекта, то дизайн отдельной строки должен быть отделен (предположим, что вам потребуется повторно использовать отображение строки позже в будущем). Поэтому я начал использовать панели стека с привязкой к данным и настраиваемые элементы управления для большинства дисплеев данных. Списки появлялись время от времени, но в основном сетка использовалась только для первичной организации страницы (заголовок, область меню, область содержимого, другие области). Ваши пользовательские объекты могут легко управлять любыми требованиями к расстоянию для каждой строки на панели стека или в сетке (одна ячейка сетки может содержать весь объект строки. Это также дает дополнительное преимущество правильной реакции на изменения ориентации, развертывание / свертывание и т. Д.

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

  <custom:MyRowObject Style="YourStyleHereOrGeneralSetter" Grid.Row="0" />
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="1" />
</Grid>

или

<StackPanel>
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="0" />
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="1" />
</StackPanel>

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

2 голосов
/ 25 июля 2012

Я сделал это прямо сейчас с одной из моих сеток.

  • Сначала примените одинаковое поле для каждого элемента в сетке. Вы можете делать это вручную, используя стили или что угодно. Допустим, вы хотите горизонтальный интервал 6px и вертикальный интервал 2px. Затем вы добавляете поля «3px 1px» к каждому дочернему элементу сетки.
  • Затем удалите поля, созданные вокруг сетки (если вы хотите выровнять границы элементов управления внутри сетки в том же положении сетки). Сделайте это, установив поле "-3px -1px" для сетки. Таким образом, другие элементы управления вне сетки будут выровнены с крайними элементами управления внутри сетки.
1 голос
/ 18 февраля 2019

У меня недавно была похожая проблема в сетке из двух столбцов, мне нужно было поле только для элементов в правом столбце. Все элементы в обоих столбцах имели тип TextBlock.

<Grid.Resources>
    <Style TargetType="{x:Type TextBlock}" BasedOn="{StaticResource OurLabelStyle}">
        <Style.Triggers>
            <Trigger Property="Grid.Column" Value="1">
                <Setter Property="Margin" Value="20,0" />
            </Trigger>
        </Style.Triggers>
    </Style>
</Grid.Resources>
1 голос
/ 26 октября 2018

Это не очень сложно. Не могу сказать, насколько сложно было в 2009 году, когда был задан вопрос, но это было тогда.

Обратите внимание, что если вы явно устанавливаете поле для дочернего элемента сетки при использовании этого решения, это поле будет отображаться в конструкторе, но не во время выполнения.

Это свойство может быть применено к Grid, StackPanel, WrapPanel, UniformGrid или любому другому потомку Panel.

PanelExt.cs

public static class PanelExt
{
    public static Thickness? GetChildMargin(Panel obj)
    {
        return (Thickness?)obj.GetValue(ChildMarginProperty);
    }

    public static void SetChildMargin(Panel obj, Thickness? value)
    {
        obj.SetValue(ChildMarginProperty, value);
    }

    /// <summary>
    /// Apply a fixed margin to all direct children of the Panel, overriding all other margins.
    /// Panel descendants include Grid, StackPanel, WrapPanel, and UniformGrid
    /// </summary>
    public static readonly DependencyProperty ChildMarginProperty =
        DependencyProperty.RegisterAttached("ChildMargin", typeof(Thickness?), typeof(PanelExt),
            new PropertyMetadata(null, ChildMargin_PropertyChanged));

    private static void ChildMargin_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var target = d as Panel;

        target.Loaded += (s, e2) => ApplyChildMargin(target, (Thickness?)e.NewValue);

        ApplyChildMargin(target, (Thickness?)e.NewValue);
    }

    public static void ApplyChildMargin(Panel panel, Thickness? margin)
    {
        int count = VisualTreeHelper.GetChildrenCount(panel);

        object value = margin.HasValue ? margin.Value : DependencyProperty.UnsetValue;

        for (var i = 0; i < count; ++i)
        {
            var child = VisualTreeHelper.GetChild(panel, i) as FrameworkElement;

            if (child != null)
            {
                child.SetValue(FrameworkElement.MarginProperty, value);
            }
        }
    }
}

Демо-версия:

MainWindow.xaml

<Grid
    local:PanelExt.ChildMargin="2"
    x:Name="MainGrid"
    >
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition Height="Auto" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Rectangle Width="100" Height="40" Fill="Red" Grid.Row="0" Grid.Column="0" />
    <Rectangle Width="100" Height="40" Fill="Green" Grid.Row="1" Grid.Column="0" />
    <Rectangle Width="100" Height="40" Fill="Blue" Grid.Row="1" Grid.Column="1" />

    <Button Grid.Row="2" Grid.Column="0" Click="NoMarginClick">No Margin</Button>
    <Button Grid.Row="2" Grid.Column="1" Click="BigMarginClick">Big Margin</Button>
    <ComboBox Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" />
</Grid>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void NoMarginClick(object sender, RoutedEventArgs e)
    {
        PanelExt.SetChildMargin(MainGrid, null);
    }
    private void BigMarginClick(object sender, RoutedEventArgs e)
    {
        PanelExt.SetChildMargin(MainGrid, new Thickness(20));
    }
}

enter image description here

enter image description here

enter image description here

1 голос
/ 16 августа 2018

Я удивлен, что еще не видел этого решения.

Исходя из Интернета, фреймворки, такие как начальная загрузка, будут использовать отрицательное поле для возврата строк / столбцов.

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

В приведенном ниже примере я использую корень StackPanel, чтобы продемонстрировать, как 3 кнопки равномерно распределены с помощью полей. Вы можете использовать другие элементы, просто измените внутренний x: Type с кнопки на ваш элемент.

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

Обновление: В некоторых комментариях от пользователя сказано, что это не работает, вот короткое видео, демонстрирующее: https://youtu.be/rPx2OdtSOYI

enter image description here

    <StackPanel>
        <Grid>
            <Grid.Resources>
                <Style TargetType="{x:Type Grid}">
                    <Setter Property="Margin" Value="-5 0"/>
                </Style>
            </Grid.Resources>

            <Grid>
                <Grid.Resources>
                    <Style TargetType="{x:Type Button}">
                        <Setter Property="Margin" Value="10 0"/>
                    </Style>
                </Grid.Resources>

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

                <Button Grid.Column="0" Content="Btn 1" />
                <Button Grid.Column="1" Content="Btn 2" />
                <Button Grid.Column="2" Content="Btn 3" />
            </Grid>

        </Grid>

        <TextBlock FontWeight="Bold" Margin="0 10">
            Test
        </TextBlock>
    </StackPanel>
...