WPFグリッドの行または列のマージンやパディングを簡単に指定できますか?
もちろん、スペースを空けるために余分な列を追加することもできますが、これはパディング/マージンの仕事のようです(XAMLが大幅に簡素化されます)。この機能を追加するために誰かが標準グリッドから派生しましたか?
WPFグリッドの行または列のマージンやパディングを簡単に指定できますか?
もちろん、スペースを空けるために余分な列を追加することもできますが、これはパディング/マージンの仕事のようです(XAMLが大幅に簡素化されます)。この機能を追加するために誰かが標準グリッドから派生しましたか?
回答:
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>
まだ誰もこれについて言及していないので、私は自分の解決策を追加すると思いました。グリッドに基づいてユーザーコントロールを設計する代わりに、スタイル宣言を使用してグリッドに含まれるコントロールをターゲットにすることができます。各要素を定義する必要なく、すべての要素にパディング/マージンを追加するので、面倒で手間がかかります。たとえば、グリッドにTextBlockしか含まれていない場合は、次のようにできます。
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Margin" Value="10"/>
</Style>
これは「セルパディング」に相当します。
Margin
進行中のプロパティの「継承」はありません。それStyle
は、aのいずれかが、そのディクショナリの所有者要素の全スコープ内のあらゆる場所のResourceDictionary
すべての要素に適用されるということです。つまり、プロパティではなく、トリクルです。TargetType
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>
データバインディングを使用している場合、カスタムコントロールもDataContextを継承します...このアプローチの私の個人的なお気に入りの利点。
ListView
コントロールの粗バージョンをGridView
モードで作成することですが、すべての「RowObjects」が同じ列幅を共有するようにするための規定はありません。実際には、グリッドとはほとんど関係がありません。
uwp(Windows10FallCreatorsUpdateバージョン以降)
<Grid RowSpacing="3" ColumnSpacing="3">
Xamarin.Forms
。
私はグリッドの1つで今それを行いました。
1つの可能性は、固定幅の行と列を追加して、探しているパディング/マージンとして機能させることです。
コンテナーのサイズによって制約されていること、およびグリッドが含まれている要素またはその指定された幅と高さと同じ大きさになることも考慮する必要があります。幅や高さを設定せずに、列と行を使用できます。これにより、デフォルトでグリッド内のスペース全体が均等に分割されます。そうすれば、要素をグリッド内で垂直方向と水平方向に中央揃えにするだけのことになります。
別の方法として、すべてのグリッド要素を、サイズとマージンが固定された単一の行と列のグリッドで固定する方法があります。グリッドに実際の要素を含む固定幅/高さボックスが含まれていること。
最近、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>
前に述べたように、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をボタンから要素に変更するだけです。
アイデアは単純です。外側のグリッドを使用して、要素のマージンを内側のグリッドの半分の量だけ(外側のマージンを使用して)境界から引き出し、内側のグリッドを使用して、必要な量の要素を均等に配置します。
更新: ユーザーからのコメントの一部は機能しないとのことです。ここでは、デモンストレーションの簡単なビデオを示しています: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>
シンプルな方法が最善の場合もあります。文字列にスペースを埋め込むだけです。数個のテキストボックスなどの場合、これは非常に簡単な方法です。
固定サイズの空白の列/行を挿入することもできます。非常にシンプルで、簡単に変更できます。