Pergunta

É facilmente possível especificar uma margem e / ou preenchimento para linhas ou colunas em uma grade WPF?

Eu poderia naturalmente adicionar colunas extras para o espaço as coisas, mas este parece ser um trabalho para preenchimento / margem (ele vai dar muito simplier XAML). Alguém derivado da grade padrão para adicionar esta funcionalidade?

Foi útil?

Solução

Você pode escrever sua própria classe GridWithMargin, herdada de Grid, e substituir o método ArrangeOverride para aplicar as margens

Outras dicas

RowDefinition e ColumnDefinition são do tipo ContentElement e Margin é estritamente uma propriedade FrameworkElement. Então, para sua pergunta, "é facilmente possível" , a resposta é um não mais definitivo. E não, eu não vi nenhum painéis de layout que demonstram esse tipo de funcionalidade.

Você pode adicionar linhas ou colunas extras como você sugeriu. Mas você também pode definir as margens em um elemento Grid si, ou qualquer coisa que iria dentro de uma Grid, de modo que o seu melhor solução para agora.

Use um controle Border fora do controle celular e definir o preenchimento para que:

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


Fonte:

Você pode usar algo como isto:

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

Ou se você não precisa as 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>

Pensei em adicionar a minha própria solução, porque ninguém ainda mencionou isso. Em vez de projetar um UserControl com base na grade, você pode direcionar os controles contidos na grade com uma declaração de estilo. Cuida de adicionar preenchimento / margem a todos os elementos sem ter que definir para cada um, que é pesado e exemplo de trabalho-intensive.For, se sua grade contém nada, mas TextBlocks, você pode fazer isso:

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

Que é como o equivalente de "preenchimento da célula".

Isso não é muito difícil. Eu não posso dizer o quão difícil foi em 2009, quando a pergunta foi feita, mas que era então.

Note que se você explicitamente definir uma margem diretamente sobre uma criança da rede ao usar esta solução, que a margem será exibido no designer, mas não em tempo de execução.

Esta propriedade pode ser aplicada à grade, StackPanel, WrapPanel, UniformGrid, ou qualquer outro descendente de Panel. Ela afeta filhos imediatos. Presume-se que as crianças vão querer gerenciar o layout de seus próprios conteúdos.

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);
            }
        }
    }
}

Demonstração:

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 descrição da imagem aqui

enter descrição da imagem aqui

enter descrição da imagem aqui

Eu corri para este problema durante o desenvolvimento de algum software recentemente e ocorreu-me perguntar por que? Por que eles fizeram isso ... a resposta estava bem ali na minha frente. A linha de dados é um objeto, por isso, se mantivermos orientação a objetos, em seguida, o projeto para uma linha específica devem ser separados (suponha que você precisa re-utilizar o ecrã de linha mais tarde no futuro). Então eu comecei a usar databound painéis pilha e controles personalizados para a maioria das telas de dados. Listas de ter feito o aparecimento ocasional, mas principalmente a rede tem sido usada apenas para organização da página principal (cabeçalho, Área de Menu, área de conteúdo, outras áreas). Seus objetos personalizados podem facilmente gerenciar todos os requisitos de espaçamento para cada linha dentro do painel de pilha ou grade (uma única célula da grade pode conter todo o objeto de linha. Isso também tem o benefício adicional de reagir adequadamente a mudanças na orientação, expandir / colapsos, etc.

<Grid>
  <Grid.RowDefinitions>
    <RowDefinition />
    <RowDefinition />
  </Grid.RowDefinitions>

  <custom:MyRowObject Style="YourStyleHereOrGeneralSetter" Grid.Row="0" />
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="1" />
</Grid>

ou

<StackPanel>
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="0" />
  <custom:MyRowObject Style="YourStyleHere" Grid.Row="1" />
</StackPanel>

Seus controles personalizados também irá herdar o DataContext se obrigatório o seu uso de dados ... meu benefício pessoal favorito desta abordagem.

Eu fiz isso agora com uma das minhas grades.

  • Primeiro aplique a mesma margem de cada elemento dentro da grade. Você pode fazer isso mannualy, usando os estilos, ou o que quiser. Vamos dizer que você quer um espaçamento horizontal de 6px e um espaçamento vertical de 2px. Então você adiciona as margens de "3px 1px" para cada criança da grade.
  • Em seguida, retire as margens criadas ao redor da grade (se você deseja alinhar as fronteiras dos controles dentro da grade para a mesma posição da grelha). Fazer esta definição de uma margem de "-3px -1px" para a rede. Dessa forma, outros controles fora da grade será alinhada com os controles outtermost dentro da grade.

Uma possibilidade seria adicionar linhas e colunas de largura fixa para atuar como o estofamento / margem que você está procurando.

Você também pode considerar que você está limitado pelo tamanho de seu recipiente, e que uma grade se tornará tão grande como o elemento que contém ou a sua largura e altura especificadas. Você poderia simplesmente usar colunas e linhas sem largura ou altura set. Dessa forma, eles padrão para quebrar uniformemente até o espaço total dentro da grade. Em seguida, ele seria apenas um mater de centrar os seus elementos verticalmente e horizontalmente dentro de você grid.

Outro método poderia ser a de envolver todos os elementos de grade em um fixo com uma única linha e grade coluna que tem um tamanho fixo e margem. Que a sua grade contém caixas de largura / altura fixas que contêm seus elementos reais.

em UWP (versão Windows10FallCreatorsUpdate e acima)

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

Editado:

Para dar margem a qualquer controle que você poderia envolver o controle com borda como este

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

Embora você não pode adicionar margem ou estofamento para uma grade, você poderia usar algo como um Frame (ou recipiente similar), que você pode aplicá-lo.

Dessa forma (se você mostrar ou ocultar o controle em uma digamos clique de botão), você não precisa adicionar margem em cada controle que pode interagir com ele.

Pense nisso como isolar os grupos de controles em unidades, em seguida, aplicar o estilo a essas unidades.

Como foi dito antes de criar uma classe GridWithMargins. Aqui está o meu exemplo de código de trabalho

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

Uso:

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

Estou surpreso eu não vi esta solução postado ainda.

Vindo da web, frameworks como inicialização usará uma margem negativa para puxar para trás as linhas / colunas.

Pode ser um pouco detalhada (embora não tão ruim), ela não funciona e os elementos são uniformemente espaçados e tamanho.

No exemplo abaixo eu usar uma raiz StackPanel para demonstrar como os 3 botões estão uniformemente espaçados utilizando margens. Você pode usar outros elementos, basta alterar o x interior:. Tipo de botão ao seu elemento

A idéia é simples, use uma grade do lado de fora para puxar as margens de elementos fora de seus limites pela metade a quantidade da rede interna (usando margens negativas), use a grelha interior para uniformemente espaço os elementos com a quantidade você quer.

Update: Alguns comentário de um usuário disse que não trabalho, aqui está uma demonstração de vídeo rápida: https://youtu.be/rPx2OdtSOYI

enter descrição da imagem aqui

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

Eu tive problema semelhante recentemente em dois grade coluna, eu precisava de uma margem sobre elementos em única coluna da direita. Todos os elementos em ambas as colunas foram do tipo 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>

Às vezes, o método simples é o melhor. pad Apenas as cordas com espaços. Se for apenas algumas caixas de texto etc Esta é de longe o método mais simples.

Você também pode simplesmente inserir em branco colunas / linhas com um tamanho fixo. Extremamente simples e você pode facilmente mudá-lo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top