是否可以轻松地为WPF网格中的行或列指定边距和/或填充?
我当然可以添加额外的列来使内容间隔开,但这似乎是一项填充/边距的工作(它将提供更简单的XAML)。是否有人从标准Grid派生来添加此功能?
是否可以轻松地为WPF网格中的行或列指定边距和/或填充?
我当然可以添加额外的列来使内容间隔开,但这似乎是一项填充/边距的工作(它将提供更简单的XAML)。是否有人从标准Grid派生来添加此功能?
Answers:
您可以编写自己的GridWithMargin
类,继承自Grid
,并重写ArrangeOverride
方法以应用边距
RowDefinition
和ColumnDefinition
是类型ContentElement
,并且Margin
严格来说是一个FrameworkElement
属性。因此,对于您的问题,“是否可能”答案是最明确的答案。而且,我还没有看到任何展示这种功能的布局面板。
您可以按照建议添加额外的行或列。但是,您也可以在Grid
元素本身或上的任何元素上设置边距Grid
,因此,这是目前最好的解决方法。
使用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>
资源:
您可以使用如下形式:
<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>
以为我会添加我自己的解决方案,因为还没有人提到这一点。您可以使用样式声明来定位包含在网格中的控件,而不是设计基于Grid的UserControl。不必在每个元素上都定义填充,而是在所有元素上添加了填充/边距,这很麻烦而且很费力,例如,如果您的Grid除了TextBlocks之外什么都不包含,您可以执行以下操作:
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10"/>
</Style>
这类似于“单元填充”。
Margin
属性,而是Style
在字典所有者元素的整个范围内,a 中的任何ResourceDictionary
元素都会应用于其TargetType
所有元素。因此,是trick流而不是财产。Style
这并不困难。我不能说在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)
{
// In real life, if we wanted to change PanelExt.ChildMargin at runtime, we
// would prefer to bind it to something, probably a dependency property of
// the view. But this will do for a demonstration.
PanelExt.SetChildMargin(MainGrid, null);
}
private void BigMarginClick(object sender, RoutedEventArgs e)
{
PanelExt.SetChildMargin(MainGrid, new Thickness(20));
}
}
build
或个run
才能显示实时预览?好又简单。1个
最近在开发某些软件时遇到了这个问题,我想问为什么?他们为什么要这么做...答案就在我眼前。一行数据是一个对象,因此,如果我们保持面向对象,则应该分开特定行的设计(假设您以后需要重新使用行显示)。因此,我开始对大多数数据显示使用数据绑定堆栈面板和自定义控件。列表偶尔出现,但大多数网格仅用于主要页面组织(标题,菜单区域,内容区域,其他区域)。您的自定义对象可以轻松管理堆栈面板或网格中每一行的任何间距要求(单个网格单元可以包含整个行对象。这还具有额外的好处,即可以对方向变化做出适当的反应,
<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>
如果您使用数据绑定,那么您的Custom控件也将继承DataContext……这是我个人最喜欢的方法。
ListView
在GridView
模式下创建WPF 控件的原始版本,但没有提供使所有“ RowObjects”共享相同列宽的条件。实际上,它不再与网格有太大关系。
在uwp中(Windows10FallCreatorsUpdate版本及更高版本)
<Grid RowSpacing="3" ColumnSpacing="3">
Xamarin.Forms
。
一种可能性是添加固定宽度的行和列,以充当您要查找的填充/边距。
您可能还认为您受到容器大小的限制,并且网格将变得与包含元素或其指定的宽度和高度一样大。您可以简单地使用没有设置宽度或高度的列和行。这样,它们默认将网格内的总空间平均分配。这样一来,您就可以在网格内将元素垂直和水平居中放置。
另一种方法可能是将所有网格元素包装在具有固定大小和边距的单行和列网格中。您的网格包含固定的宽度/高度框,其中包含您的实际元素。
我最近在两列网格中遇到了类似的问题,我只需要在右列中的元素上留一点空白。两列中的所有元素均为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>
如前所述,创建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>
令我惊讶的是我还没有看到这个解决方案。
来自网络的引导程序之类的框架将使用负边距来拉回行/列。
它可能有点冗长(尽管还不错),但确实有效,并且元素的间距和大小均均匀。
在下面的示例中,我使用 StackPanel
根来演示如何使用边距均匀地分布3个按钮。您可以使用其他元素,只需将内部x:Type从button更改为您的元素。
这个想法很简单,在外部使用网格将元素的边距拉出边界的一半,而不是内部网格的量(使用负边距),然后使用内部网格将元素与所需的量均匀地隔开。
更新: 用户的一些评论说它不起作用,这是一个快速视频演示: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>