it-roy-ru.com

WPF: Сетка с полем столбец/строка/отступы?

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

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

113
Brad Leach

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

16
Thomas Levesque

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

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

71
Charlie

Используйте элемент управления 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>


Источник:

28
samad

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

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

Или, если вам не нужны TemplateBindings:

<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>
19
JayGee

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

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

Что походит на эквивалент "дополнения ячейки".

4
James M

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

<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, если вы используете привязку данных ... мое личное любимое преимущество этого подхода.

3
Matt Meikle

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

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

в uwp (версия Windows10FallCreatorsUpdate и выше)

<Grid RowSpacing="3" ColumnSpacing="3">
1
Kursat Turkay

Одна из возможностей - добавить строки и столбцы фиксированной ширины, которые будут действовать как искомый отступ/отступ. 

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

Другой метод может заключаться в том, чтобы обернуть все элементы сетки в фиксированную сетку из одной строки и столбца, которая имеет фиксированный размер и поле. Что ваша сетка содержит фиксированные поля ширины/высоты, которые содержат ваши фактические элементы.

1
Adam Lenda

Как было сказано ранее, создайте класс GridWithMargins . Вот мой пример рабочего кода

public class GridWithMargins : Grid
{
    public Thickness RowMargin { get; set; } = new Thickness(10, 10, 10, 10);
    protected override Size ArrangeOverride(Size arrangeSize)
    {
        var basesize = base.ArrangeOverride(arrangeSize);

        foreach (UIElement child in InternalChildren)
        {
            var pos = GetPosition(child);
            pos.X += RowMargin.Left;
            pos.Y += RowMargin.Top;

            var actual = child.RenderSize;
            actual.Width -= (RowMargin.Left + RowMargin.Right);
            actual.Height -= (RowMargin.Top + RowMargin.Bottom);
            var rec = new Rect(pos, actual);
            child.Arrange(rec);
        }
        return arrangeSize;
    }

    private Point GetPosition(Visual element)
    {
        var posTransForm = element.TransformToAncestor(this);
        var areaTransForm = posTransForm.Transform(new Point(0, 0));
        return areaTransForm;
    }
}

Использование:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.Microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:WpfApplication1"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:GridWithMargins ShowGridLines="True">
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Rectangle Fill="Red" Grid.Row="0" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
            <Rectangle Fill="Green" Grid.Row="1" Grid.Column="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
            <Rectangle Fill="Blue" Grid.Row="1" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
        </local:GridWithMargins>
    </Grid>
</Window>
1
Paul Baxter

У меня недавно была похожая проблема в сетке из двух столбцов, мне нужно было поле только для элементов в правом столбце. Все элементы в обоих столбцах имели тип 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
Miloš Auder

Чтобы добавить поле в сетку, вы можете обернуть сетку границей, как это

<!--...-->
    <Border Padding="10">
            <Grid>
<!--...-->
0
Juan Pablo

Это не очень сложно. Не могу сказать, насколько сложно было в 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

0
Ed Plunkett

Хотя вы не можете добавить поле или отступ в Grid, вы можете использовать что-то вроде фрейма (или аналогичного контейнера), к которому вы можете применить его.

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

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

0
ed13

Я удивлен, что еще не видел это решение опубликовано.

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

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

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

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

Update: В некоторых комментариях от пользователя сказано, что это не работает, вот краткое видео, демонстрирующее: 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>
0
AntonB