WPF:具有列/行边距/填充的网格?

时间:2009-08-24 00:28:30

标签: wpf grid padding margin

是否可以轻松地为WPF网格中的行或列指定边距和/或填充?

我当然可以添加额外的列来解决问题,但这似乎是填充/边距的工作(它会给很多更简单的XAML)。有人从标准网格派生出来添加此功能吗?

16 个答案:

答案 0 :(得分:71)

RowDefinitionColumnDefinition属于ContentElementMargin严格属于FrameworkElement属性。所以对于你的问题,“很容易”答案是最明确的否定。不,我没有看到任何展示这种功能的布局面板。

您可以按照建议添加额外的行或列。但您也可以在Grid元素本身或Grid内的任何内容上设置边距,这是您现在最好的解决方法。

答案 1 :(得分:32)

在单元格控件外使用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>


来源:

答案 2 :(得分:18)

您可以使用以下内容:

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

答案 3 :(得分:16)

您可以编写自己的GridWithMargin类,继承自Grid,并覆盖ArrangeOverride方法以应用边距

答案 4 :(得分:5)

以为我添加了自己的解决方案,因为还没有人提到这个。您可以使用样式声明来定位网格中包含的控件,而不是基于Grid设计UserControl。注意为所有元素添加填充/边距而不必为每个元素定义,这是繁琐且劳动密集的。例如,如果你的Grid只包含TextBlocks,你可以这样做:

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

这相当于&#34; cell padding&#34;。

答案 5 :(得分:3)

我最近在开发一些软件时遇到了这个问题,我想问为什么?他们为什么要这样做......答案就在我面前。一行数据是一个对象,因此如果我们保持面向对象,那么应该分离特定行的设计(假设您将来需要重新使用行显示)。所以我开始使用数据绑定堆栈面板和自定义控件来进行大多数数据显示。列表偶尔出现,但主要是网格仅用于主页面组织(页眉,菜单区域,内容区域,其他区域)。您的自定义对象可以轻松管理堆栈面板或网格中每一行的任何间距要求(单个网格单元格可以包含整个行对象。这还具有对方向更改,扩展/折叠等作出适当反应的额外好处。

<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 ......这是我个人最喜欢的方法。

答案 6 :(得分:2)

我现在用我的一个网格做了。

  • 首先将相同的边距应用于网格内的每个元素。您可以使用样式或任何您喜欢的方式进行此操作。假设您希望水平间距为6px,垂直间距为2px。然后将“3px 1px”的边距添加到网格的每个子节点。
  • 然后删除在网格周围创建的边距(如果要将网格内控件的边框与网格的相同位置对齐)。这样做可以为网格设置“-3px -1px”的边距。这样,网格外的其他控件将与网格内的outtermost控件对齐。

答案 7 :(得分:1)

一种可能性是添加固定宽度的行和列作为您要查找的填充/边距。

您可能还会认为您受到容器大小的限制,并且网格将变得与包含元素或其指定的宽度和高度一样大。您可以简单地使用没有设置宽度或高度的列和行。这样他们默认会均匀地分解网格中的总空间。然后它就是在你的网格中垂直和水平对齐你的元素的主要部分。

另一种方法可能是将所有网格元素包装在一个固定的单行&amp;具有固定大小和边距的列网格。您的网格包含固定的宽度/高度框,其中包含您的实际元素。

答案 8 :(得分:1)

如前所述,创建一个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>

答案 9 :(得分:1)

在uwp(Windows10FallCreatorsUpdate版本及以上版本)

var rawData = new List<Tuple<string, List<string>>>
{
    Tuple.Create<string,List<string>> ("Cat", new List<string> { "Cheetah", "Lion" })
};

答案 10 :(得分:1)

我最近在两列网格中遇到了类似的问题,我只需要在右列的元素上留出一定的空白。两列中的所有元素均为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>

答案 11 :(得分:0)

虽然您无法为网格添加边距或填充,但您可以使用类似框架(或类似容器)的东西,您可以将其应用到。

这样(如果你在按钮上显示或隐藏控件点击说),你就不需要在每个可能与之交互的控件上添加边距。

将其视为将控件组隔离为单元,然后将样式应用于这些单元。

答案 12 :(得分:0)

很惊讶,我还没有看到此解决方案发布。

来自网络的引导程序之类的框架将使用负边距拉回行/列。

它可能有点冗长(尽管还不错),它确实可以工作,并且元素的间距和大小均匀。

在下面的示例中,我使用StackPanel根来演示如何使用边距均匀分布3个按钮。您可以使用其他元素,只需将内部x:Type从button更改为元素。

这个想法很简单,在外部使用网格将元素的边距拉出其边界的距离是内部网格的一半(使用负边距),使用内部网格将元素与元素的间隔均匀你想要的。

更新: 用户的一些评论说这行不通,下面是一个简短的视频演示: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>

答案 13 :(得分:0)

这并不困难。我不能说在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

答案 14 :(得分:0)

要给网格留有余量,您可以像这样用边框包裹网格

<!--...-->
    <Border Padding="10">
            <Grid>
<!--...-->

答案 15 :(得分:-5)

有时简单的方法是最好的。用空格填充你的字符串。如果它只是几个文本框等,这是迄今为止最简单的方法。

您也可以简单地插入固定大小的空白列/行。非常简单,您可以轻松更改它。