【问题标题】:WPF: Grid with column/row margin/padding?WPF:带有列/行边距/填充的网格?
【发布时间】:2010-11-22 03:24:35
【问题描述】:

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

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

【问题讨论】:

  • 一个有用的例子可以在这里找到:codeproject.com/Articles/107468/WPF-Padded-Grid
  • 有点困惑,这不是网格基线功能的一部分......
  • 截至今天,10 年的答案证明,事实是这并不容易,最好的办法(避免每次使用单元格时额外的容易出错的工作)是@ 987654322@(如 @peter70 之前建议的那样)添加适当的单元格填充依赖属性,该属性将控制单元格子的 Margin 属性。这不是一项漫长的任务,然后你就有了一个可重用的控件。旁注... Grid 确实是一个设计不佳的控件。

标签: wpf grid padding margin


【解决方案1】:

RowDefinitionColumnDefinition 属于 ContentElement 类型,而 Margin 严格来说是 FrameworkElement 属性。所以对于你的问题,“这很容易吗?” 答案是肯定的。不,我还没有看到任何展示这种功能的布局面板。

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

【讨论】:

  • OP 未尝试在 RowDefinition 或 ColumnDefinition 上设置边距。他正试图在从 FrameworkElement 派生的可视子网格上设置边距。
【解决方案2】:

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


来源:

【讨论】:

  • @RichardEverett Check the Way Back Machine:链接已在答案中更新。
  • 我最喜欢这个答案。它为原始问题提供了解决方案,而且很简单。谢谢!
  • 这个简单实用
  • 当我第一次添加时,没有任何改变,但在运行应用程序后它更新并且运行良好。
【解决方案3】:

你可以使用这样的东西:

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

【讨论】:

  • 感谢 JayGee,但此解决方案适用于 DataGrid,而不是标准 Grid 控件。
  • 点击填充空间不再选择行。
【解决方案4】:

我想添加自己的解决方案,因为还没有人提到这一点。您可以使用样式声明来定位网格中包含的控件,而不是基于 Grid 设计 UserControl。负责为所有元素添加填充/边距,而不必为每个元素定义,这既麻烦又费力。例如,如果您的 Grid 只包含 TextBlocks,您可以这样做:

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

相当于“单元格填充”。

【讨论】:

  • 这会涓涓细流吗?例如,如果您在网格行中有一个 stackpanel,stackpanel 的 textblock 子项是否继承此属性?
  • 不确定它是否会流过直系子女,但您可以通过一个简单的测试来找出答案。
  • @Maslow 答案绝对是“是的”,但你的措辞有点误导。没有“继承”Margin 属性,而是ResourceDictionary 中的任何Style 都将应用于其TargetType 的每个元素在整个范围内 该字典的所有者元素。所以它是Style,而不是属性。
【解决方案5】:

已编辑:

要给任何控件留出边距,您可以像这样用边框包裹控件

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

【讨论】:

  • OP 问题不是在网格周围有一个边距,而是在网格单元周围有一个边距(或者实际上在行和列周围询问,后来与“添加额外的列/行相矛盾这正是我试图避免的”评论)。
【解决方案6】:

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

【讨论】:

  • 我不明白为什么人们会给你竖起大拇指,因为它远没有你在这里想象/描述的那么容易。只要试着做 30 分钟,你很快就会发现你的答案是不适用的。还要考虑行和列的跨越
【解决方案7】:

在uwp中(Windows10FallCreatorsUpdate版本及以上)

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

【讨论】:

  • 这也适用于Xamarin.Forms
  • UWP != WPF... 为什么 cmets 需要 15 个字符长(反问)。
  • ​这里有 14 个字符。
【解决方案8】:

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

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

【讨论】:

  • 您在这里尝试做的是在GridView 模式下创建 WPF ListView 控件的粗略版本,但没有规定让所有“RowObjects”共享相同的列宽.实际上,它不再与 Grid 有太大关系。
【解决方案9】:

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

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

【讨论】:

  • 对网格内的每个元素应用相同的边距似乎是最简单的方法。
【解决方案10】:

我很惊讶我还没有看到这个解决方案。

来自网络,像 bootstrap 这样的框架将使用负边距来拉回行/列。

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

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

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

更新: 一些用户的评论说它不起作用,这里有一个快速视频演示:https://youtu.be/rPx2OdtSOYI

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

【讨论】:

  • 我的错误——我没有注意到隐含的 Button 样式。负边距让我分心了。
【解决方案11】:

我最近在两列网格中遇到了类似的问题,我只需要右列元素的边距。两列中的所有元素都是 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>

【讨论】:

    【解决方案12】:

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

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

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

    【讨论】:

    • 感谢 Adam,但是添加额外的列/行正是我试图避免的。我只是想减少我的标记,并且能够指定边距或填充将对此有所帮助。
    • 您只需要定义额外的列和行,而不是为它们添加标记。例如,如果您想在 3 列的列之间添加 N 宽度,您将有 5 个列定义。第一个是自动宽度,下一个是固定的,然后是自动的,然后是固定的,然后是自动的。然后仅将第 1、3 和 5 列分配给实际内容元素。我明白你关于额外列标记的观点,但除非你有数百个元素,否则这对我来说似乎微不足道。你的电话虽然。另外,您是否尝试过使用设置了边距的堆栈面板的堆栈面板?
    • 就我而言,添加“拆分器/边距”列是迄今为止最干净和最简单的。谢谢!
    【解决方案13】:

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

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

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

    【讨论】:

      【解决方案14】:

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

      【讨论】:

      • 抛出 System.ArgumentException: '宽度必须为非负数。' actual.Width -= Math.Min(actual.Width, RowMargin.Left + RowMargin.Right); actual.Height -= Math.Min(actual.Height, RowMargin.Top + RowMargin.Bottom);
      • 通过 Jamie 的修复,它运行了,但仍然没有做它应该做的事情。将行高和列宽设置为“自动”时,它会以不同的方式做错事。
      【解决方案15】:

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

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

      【讨论】:

      • 可能是因为这不是一个很好的方法。使用空格可能是“简单的出路”,但实际上它更像是一种黑客攻击。这并不精确,它可能(并且可能会)在具有不同缩放比例的监视器上以不同且不可靠的方式呈现,并且您无法控制所创建的确切空间量。插入空白列/行会使您的网格混乱......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-13
      • 2013-03-04
      • 1970-01-01
      • 2015-06-09
      相关资源
      最近更新 更多