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

168

在WPF Grid中,是否容易指定行或列的边距(margin)和/或填充(padding)?

我当然可以添加额外的列来间隔控件,但这似乎是填充或边距的工作(它将使XAML简单得多)。有没有人从标准Grid派生出来以添加这个功能?


4
一个有用的例子可以在这里找到:http://www.codeproject.com/Articles/107468/WPF-Padded-Grid - peter70
7
有点困惑,为什么这不是Grid基础功能的一部分... - uceumern
2
截至今天,通过10年的答案证明,事实是很难轻松实现,最好的方法是派生网格(如@peter70之前建议的)添加适当的单元格填充依赖属性,该属性将控制单元格子项的Margin属性。这不是一个长时间的任务,然后你就有了一个可重用的控件。顺便说一句...网格真的是一个设计非常糟糕的控件。 - mins
15个回答

98

RowDefinitionColumnDefinition属于ContentElement类型,而Margin是严格属于FrameworkElement属性。因此,对于您的问题,“是否轻松实现”,答案是绝对不可能的。我也没有见过任何布局面板演示这种功能。

您可以按照您提出的建议添加额外的行或列。但是,您也可以设置Grid元素本身或任何放置在Grid内部的元素的边距,因此这是目前最好的解决方法。


3
OP并没有试图在RowDefinition或ColumnDefinition上设置边距。他试图在网格的可视子元素上设置边距,这些子元素派生自FrameworkElement。 - 15ee8f99-57ff-4f92-890c-b56153

89

使用一个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
@RichardEverett请检查Way Back Machine:链接已在答案中更新。 - CJBS
2
我最喜欢这个答案。它提供了原始问题的解决方案并且很直观。谢谢! - Tobias
2
这很简单实用。 - aggsol
当我第一次添加这个时,没有任何变化,但在运行应用程序后它更新了并且完美地工作了。 - ELIAS YOUSSEF

20

你可以使用类似这样的代码:

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

32
谢谢JayGee,然而这个解决方案适用于DataGrid控件,而不是标准Grid控件。 - Brad Leach
点击填充空间不再选择行。 - jor

8

我想分享一下我的解决方案,因为目前还没有人提到这个。不需要基于Grid设计UserControl,而是可以使用样式声明来定位包含在Grid中的控件。这样可以为所有元素添加填充/边距,而无需为每个元素定义,这样会很麻烦和费力。例如,如果你的Grid只包含TextBlock,可以这样做:

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

这就像是“单元格内边距”的等效物。


这个会往下传递吗?例如,如果你在网格行中有一个堆栈面板,那么堆栈面板的文本块子元素会继承这个属性吗? - Maslow
不确定它是否会传递给直接子元素,但您可以通过简单的测试找出答案。 - user1618054
2
@Maslow 答案绝对是“是的”,但你的措辞有点误导。没有发生Margin属性的“继承”,而是在ResourceDictionary中的任何Style将应用于其TargetType的每个元素,无论在该字典所有者元素的整个范围内的哪个位置。因此,是Style会渗透,而不是属性。 - Glenn Slayden

4

我很惊讶没有看到这个解决方案被发布。

从Web出发,像Bootstrap这样的框架将使用负边距来拉回行/列。

可能有点啰嗦(尽管不是那么糟糕),它确实有效,并且元素是均匀间隔和大小的。

在下面的示例中,我使用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>

4

编辑:

如果要给任何控件添加边距,您可以像这样用边框包装控件

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

1
OP的问题不是在网格周围有边距,而是在网格单元格周围有边距(或者如实际所要求的,在行和列周围有边距,后来被“添加额外的列/行正是我试图避免的”评论所否认)。 - mins

2
最近我在开发一些软件时遇到了这个问题,于是我想问为什么?他们为什么要这样做...答案就在我的面前。 一行数据就是一个对象,因此如果我们保持对象的特性,那么对于特定行的设计应该是分离的(假设你需要将来重新使用行显示)。 因此,我开始使用数据绑定的堆栈面板和自定义控件来显示大部分数据。 列表偶尔会出现,但大多数情况下网格仅用于主页面组织(标题、菜单区域、内容区域、其他区域)。 您的自定义对象可以轻松管理堆栈面板或网格中每行的任何间距要求(一个单独的网格单元格可以包含整个行对象)。 这也具有对方向变化、展开/折叠等变化的正确反应的附加好处。
<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...这是我个人最喜欢的好处。


1
你在这里尝试创建一个简单版本的WPF ListView控件,但没有提供使所有“RowObjects”共享相同列宽的功能。实际上,它与网格不再有太多关系。 - Glenn Slayden

2
最近我在使用双列网格时遇到了类似的问题,我需要在右列元素上添加一个边距。两列中的所有元素都是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>

2

我现在已经使用我的网格之一完成了这个操作。

  • 首先将相同的边距应用于网格内的每个元素。您可以手动执行此操作,使用样式或任何您喜欢的方式。假设您想要水平间距为6px,垂直间距为2px。然后,您需要向网格的每个子元素添加“3px 1px”的边距。
  • 然后删除围绕网格创建的边距(如果您想要将网格内控件的边框与网格的相同位置对齐)。通过将网格的边距设置为“-3px -1px”来执行此操作。这样,网格外的其他控件将与网格内最外层的控件对齐。

将相同的边距应用于网格内的每个元素似乎是最简单的方法。 - Envil

1

在UWP(Windows 10秋季创作者更新版本及以上)中

<Grid RowSpacing="3" ColumnSpacing="3">

1
这同样适用于 Xamarin.Forms - user1618054
13
UWP不等于WPF...为什么评论需要15个字符长(反问)。 - Richard Moore
1
这里有14个字符。 - Jogge

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接