web-dev-qa-db-ja.com

WPF:列/行マージン/パディングのグリッド?

WPFグリッドの行や列にマージンやパディングを簡単に指定できますか?

もちろん、余分な列を追加してスペースを空けることもできますが、これはパディング/マージンの仕事のようです(muchシンプルなXAMLを提供します)。この機能を追加するために標準のグリッドから派生した人はいますか?

122
Brad Leach

GridWithMarginから継承した独自のGridクラスを作成し、ArrangeOverrideメソッドをオーバーライドしてマージンを適用できます

16
Thomas Levesque

RowDefinitionおよびColumnDefinitionContentElement型であり、Marginは厳密にFrameworkElementプロパティです。だからあなたの質問に、"簡単に可能ですか"答えは最も明確なノーです。いいえ、この種の機能を示すレイアウトパネルを見たことはありません。

提案どおりに行または列を追加できます。ただし、Grid要素自体、またはGrid内に収まるものにマージンを設定することもできるため、現時点での最善の回避策です。

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


ソース:

37
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>
18
JayGee

誰もまだこれについて言及していないので、私は自分のソリューションを追加すると思いました。グリッドに基づいてUserControlを設計する代わりに、スタイル宣言を使用してグリッドに含まれるコントロールをターゲットにできます。面倒で手間がかかる各要素を定義せずに、すべての要素にパディング/マージンを追加します。たとえば、グリッドにTextBlocksしか含まれていない場合、これを行うことができます。

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

これは「セルパディング」に相当します。

6
James M

これはそれほど難しくありません。質問が出された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

5
Ed Plunkett

最近、いくつかのソフトウェアの開発中にこの問題に遭遇しましたが、なぜそうなったのでしょうか?なぜ彼らはこれをしたのか...答えは私の目の前にあった。データの行はオブジェクトであるため、オブジェクトの方向を維持する場合は、特定の行のデザインを分離する必要があります(将来、行表示を再利用する必要がある場合)。そこで、ほとんどのデータ表示にデータバインドスタックパネルとカスタムコントロールを使い始めました。リストはときどき表示されますが、ほとんどの場合、グリッドはプライマリページの編成(ヘッダー、メニューエリア、コンテンツエリア、その他のエリア)にのみ使用されます。カスタムオブジェクトは、スタックパネルまたはグリッド内の各行の間隔要件を簡単に管理できます(単一のグリッドセルに行オブジェクト全体を含めることができます。これには、方向の変更、展開/折りたたみなどに適切に反応するという追加の利点もあります。

<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

グリッドの1つを使用して、それを実行しました。

  • 最初に、グリッド内のすべての要素に同じマージンを適用します。スタイルを使用するなど、好きな方法でこの方法を使用できます。 6pxの水平方向の間隔と2pxの垂直方向の間隔が必要だとしましょう。次に、グリッドのすべての子に「3px 1px」のマージンを追加します。
  • 次に、グリッドの周囲に作成されたマージンを削除します(グリッド内のコントロールの境界線をグリッドの同じ位置に揃える場合)。グリッドに「-3px -1px」のマージンを設定して、これを実行します。これにより、グリッドの外側にある他のコントロールは、グリッド内の最も外側のコントロールに揃えられます。
2
isierra

uwp(Windows10FallCreatorsUpdateバージョン以上)

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

1つの可能性は、探しているパディング/マージンとして機能する固定幅の行と列を追加することです。

また、コンテナのサイズに制約されていること、およびグリッドが含まれている要素またはその指定された幅と高さと同じ大きさになることを考慮することもできます。幅や高さを設定せずに列と行を使用するだけで済みます。そのようにして、デフォルトでグリッド内の合計スペースを均等に分割します。次に、グリッド内で要素を垂直方向および水平方向に中央に配置するだけです。

別の方法は、サイズとマージンが固定された単一の行と列のグリッドですべてのグリッド要素をラップすることです。グリッドに、実際の要素を含む固定幅/高さのボックスが含まれていること。

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

このソリューションがまだ投稿されていないことに驚きました。

ウェブから来ると、bootstrapのようなフレームワークは負のマージンを使用して行/列を引き戻します。

少し冗長かもしれませんが(それほど悪くはありませんが)、機能し、要素の間隔とサイズは均等です。

以下の例では、StackPanelルートを使用して、マージンを使用して3つのボタンが均等に配置される方法を示します。他の要素を使用できます。内側のx:Typeをボタンから要素に変更するだけです。

考え方は簡単です。外側のグリッドを使用して、要素のマージンを内側のグリッドの半分の量だけ(負のマージンを使用して)境界から引き出し、内側のグリッドを使用して必要な量の要素を均等に配置します。

更新:ユーザーからのコメントで、機能しないとのコメントがありました。以下に、簡単なビデオを示します: 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>
1
AntonB

最近、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>
1
Miloš Auder

編集済み:

コントロールにマージンを与えるには、次のように境界線でコントロールをラップできます

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

グリッドに余白やパディングを追加することはできませんが、フレーム(または同様のコンテナー)のようなものを使用して、それを適用できます。

そのようにすると(ボタンのクリックでコントロールを表示または非表示にする場合)、操作する可能性のあるすべてのコントロールにマージンを追加する必要がなくなります。

コントロールのグループをユニットに分離し、それらのユニットにスタイルを適用すると考えてください。

0
ed13