WPF: Сітка з полями стовпців / рядків / прокладками?


137

Чи легко можна вказати поле та / або прокладку для рядків чи стовпців у WPF Grid?

Звичайно, я міг би додати додаткові стовпці до космічних речей, але це здається роботою для прокладки / поля (це дасть набагато простіший XAML). Хтось із стандартної Grid додав цю функціональність?


4
Один корисний приклад ви можете знайти тут: codeproject.com/Articles/107468/WPF-Padded-Grid
peter70

2
Деяка спантеличено, що це не є частиною
основної

На сьогоднішній день, продемонстрований десятирічними відповідями, правда - це неможливо легко, а найкраще зробити (щоб уникнути додаткової роботи, схильної до помилок щоразу, коли використовується комірка) - це отримати Grid (як раніше запропонував @ peter70 ) додати відповідне властивість залежності прокладки комірок, яке буде контролювати властивість Margin дочірки клітини. Це не довге завдання, і тоді у вас є контроль багаторазового використання. Бічний коментар ... Сітка справді погано розроблена система управління.
хвилини

Відповіді:


10

Ви можете написати власний GridWithMarginклас, успадкований від цього Grid, і замінити ArrangeOverrideметод застосування поля


32
Я не розумію, чому люди дають вам великі пальці, оскільки це далеко не так просто, як ви думаєте / описуєте тут. Просто спробуйте це зробити 30 хвилин, і ви швидко побачите, що ваша відповідь є непридатною. Також подумайте про розміщення рядків і стовпців
Ерік

89

RowDefinitionі ColumnDefinitionмають тип ContentElement, і Marginсуворо є FrameworkElementвласністю. Тож до вашого питання "чи легко це можливо" відповідь - це абсолютно визначене "ні". І ні, я не бачив жодної панелі компонування, яка демонструвала б такий функціонал.

Ви можете додати додаткові рядки або стовпці, як запропонували. Але ви також можете встановити поле для Gridсамого елемента або будь-якого, що входитиме всередину a Grid, тож це ваш найкращий спосіб вирішити наразі.


OP не намагається встановити маржу для RowDefinition або ColumnDefinition. Він намагається встановити поле для візуальних дітей сітки, які випливають із FrameworkElement.
15ee8f99-57ff-4f92-890c-b56153

58

Використовуйте 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>


Джерело:


2
@ RichardEverett Перевірте машину зворотного шляху: посилання оновлено у відповідь.
CJBS

Мені найбільше подобається ця відповідь. Це забезпечує вирішення оригінального питання і прямо вперед. Дякую!
Тобіас

Це легко і практично
aggsol

19

Ви можете використовувати щось подібне:

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

Або якщо вам не потрібні прив'язки шаблонів:

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

28
Дякую JayGee, однак це рішення призначене для DataGrid - не стандартного Grid управління.
Бред Ліч

Натиснувши на пробіл, ви не будете вибирати рядок більше.
Джор

9

Думав, я б додав власне рішення, тому що про це ще ніхто не згадував. Замість того, щоб створити UserControl на основі Grid, ви можете орієнтуватися на елементи управління, що містяться в сітці, із оголошенням стилю. Подбайте про додавання padding / margin до всіх елементів, не визначаючи для кожного, що є громіздким і трудомістким. Наприклад, якщо ваш Grid не містить нічого, крім TextBlocks, ви можете зробити це:

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

Що на зразок еквівалента "забивання клітин".


ця струмка вниз? Наприклад, якщо у вас є стекпанель у рядку сітки, чи дітям текстового блоку стекпанеля успадковується ця властивість?
Маслоу

Не впевнений, чи це обмацує минулих дітей, але це можна дізнатися за допомогою простого тесту.
Джеймс М

2
@Maslow Відповідь абсолютно "Так", але ваше формулювання трохи оманливе. Не відбувається "успадкування" Marginвластивості, адже будь-яке Styleв ResourceDictionaryзаповіті застосовуватиметься до кожного його елемента TargetTypeскрізь у межах усієї області власника цього словника. Отже, це те, Styleщо трюкає, а не властивість.
Гленн Слейден

8

Це не страшно складно. Я не можу сказати, як важко було в 2009 році, коли було задано питання, але це було тоді.

Зауважте, що якщо ви явно встановлюєте поле безпосередньо на дочірній сітці під час використання цього рішення, це поле з’явиться в дизайнері, але не під час виконання.

Цю властивість можна застосувати до Grid, StackPanel, WrapPanel, UniformGrid або будь-якого іншого нащадка панелі. Це впливає на безпосередніх дітей. Передбачається, що діти захочуть керувати компоновкою власного вмісту.

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

введіть тут опис зображення

введіть тут опис зображення

введіть тут опис зображення


Чи знаєте ви, чому Ваша відповідь спростована? Схоже , робочого вирішення проблеми ФП в
Thesystem

4
@thesystem Я думаю, що хтось з-за чогось засмутився. Не велике діло. Але, можливо, в ньому є помилка, яку я пропустив. Це буває.
15ee8f99-57ff-4f92-890c-b56153

Отримав dw, тому що для цього потрібен попередній buildабо runпопередній перегляд? Хороший і простий. 1 вгору.
Meow Cat 2012,

3

Я зіткнувся з цією проблемою, коли нещодавно розробляв якесь програмне забезпечення, і у мене виникло питання, Чому? Чому вони це зробили ... відповідь була прямо перед мною. Рядок даних - це об'єкт, тому якщо ми підтримуємо орієнтацію на об'єкт, тоді дизайн для певного рядка повинен бути відокремлений (припустимо, вам потрібно буде повторно використовувати відображення рядків пізніше в майбутньому). Тому я почав використовувати панелі стекових даних та персональні елементи керування для більшості дисплеїв даних. Списки мали випадковий вигляд, але в основному сітка використовувалася лише для первинної організації сторінки (заголовка, області меню, області вмісту, інших областей). Ваші користувацькі об'єкти можуть легко керувати будь-якими вимогами до інтервалу для кожного рядка на панелі стека чи сітки (одна комірка сітки може містити весь об’єкт рядка. Це також має додаткову перевагу від правильного реагування на зміни орієнтації,

<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керування WPF в GridViewрежимі, але не маючи можливості для того, щоб усі "RowObjects" мали однакову ширину стовпців. Насправді це більше не має нічого спільного з Grid.
Гленн Слейден


3

Відредаговано:

Щоб надати поле будь-якому контролю, ви можете обернути елемент керування таким чином

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

Проблема ОП полягає не у тому, щоб мати поле навколо сітки, а поля навколо комірок сітки (або, як насправді запитували навколо рядків та стовпців, пізніше суперечить " додавання додаткових стовпців / рядків - саме те, що я намагався уникнути " коментар) .
хвилини

2

Я зробив це зараз однією з моїх сіток.

  • Спочатку застосуйте однаковий запас до кожного елемента всередині сітки. Ви можете зробити це щорічно, використовуючи стилі або все, що завгодно. Скажімо, вам потрібно горизонтальний інтервал 6 пікселів і вертикальний інтервал 2 пікселя. Потім ви додаєте поля "3px 1px" до кожної дитини сітки.
  • Потім видаліть поля, створені навколо сітки (якщо ви хочете вирівняти межі елементів управління всередині сітки до того ж положення сітки). Виконайте це встановлення запасу "-3px -1px" до сітки. Таким чином, інші елементи керування поза сіткою будуть вирівняні з зовнішніми елементами управління всередині сітки.

Застосовувати однаковий запас до кожного елемента всередині сітки, здається, найпростіший спосіб.
Енвіл

1

Однією з можливостей буде додавання рядків та стовпців із фіксованою шириною, щоб вони виконували роль прокладки / поля, яку ви шукаєте.

Ви також можете врахувати, що вас обмежує розмір вашого контейнера і що сітка стане такою ж великою, як елемент, що містить, або його задана ширина та висота. Ви можете просто використовувати стовпці та рядки без встановленої ширини чи висоти. Таким чином вони за замовчуванням рівномірно розподіляють загальний простір в сітці. Тоді це було б просто центром центрування ваших елементів вертикально і горизонтально всередині вас.

Іншим методом може бути обгортання всіх елементів сітки у фіксованій сітці з одним рядком та стовпцями, яка має фіксований розмір та поле. Щоб ваша сітка містила фіксовану ширину / висоту вікон, які містять ваші фактичні елементи.


1
Дякую Адамові, проте додавання додаткових стовпців / рядків саме те, що я намагався уникнути. Я просто хочу зменшити розмітку, і в цьому зможе вказати поле або запас.
Бред Ліч

Вам потрібно лише визначити зайві стовпці та рядки, а не додавати позначку для них. Наприклад, якщо ви хочете додати N ширини між стовпцями для 3 стовпців, у вас буде 5 визначень стовпців. Першим буде ширина авто, а наступний фіксований, потім автоматичний, потім виправлений, потім автоматичний. Потім призначайте стовпці 1, 3 та 5 фактичним елементам вмісту. Я бачу вашу думку щодо додаткової розмітки стовпців, але якщо у вас є сотні елементів, це здається мені тривіальним. ваш дзвінок, хоча. Також ви спробували використовувати панель стеків панелей стеків із встановленими полями?
Адам Ленда

У моєму випадку додавання стовпчика "спліттер / маржа" було на сьогодні найчистішим та найпростішим. Дякую!
jchristof

1

У мене подібні проблеми були нещодавно у сітці двох стовпців, мені потрібен запас лише для елементів у правій колонці. Усі елементи в обох стовпцях мали тип 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>

0

Хоча ви не можете додати поля або накладки до Grid, ви можете використовувати щось на зразок Frame (або подібного контейнера), до якого ви можете застосувати його.

Таким чином (якщо ви показуєте або ховаєте елемент керування на натисканні кнопки сказати), вам не потрібно буде додавати поле для кожного елемента управління, який може взаємодіяти з ним.

Подумайте про це як про виділення груп елементів управління на одиниці, а потім застосуйте стиль до цих одиниць.


0

Як було зазначено раніше, створюйте клас 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
Throw System.ArgumentException: "Ширина повинна бути негативною." фактична ширина - = Math.Min (фактична ширина, RowMargin.Left + RowMargin.Right); фактична висота - = Math.Min (фактична. Висота, RowMargin.Top + RowMargin.Bottom);
Джеймі Меллуей

З виправленням Джеймі він працює, але все ще не робить того, що повинен. Якщо висота рядка та ширина стовпця встановлені на Авто, це робить неправильну справу по-іншому.
15ee8f99-57ff-4f92-890c-b56153

0

Я здивований, що я ще не бачив цього рішення розміщеним.

Починаючи з Інтернету, такі рамки, як завантажувальна програма, використовуватимуть негативний запас для відведення рядків / стовпців.

Це може бути трохи багатослівним (хоч і не дуже погано), але воно працює, і елементи рівномірно розміщені та розміри.

У наведеному нижче прикладі я використовую StackPanelкорінь, щоб продемонструвати, як 3 кнопки рівномірно розташовані за допомогою поля. Ви можете використовувати інші елементи, просто змініть внутрішній х: Введіть з кнопки на ваш елемент.

Ідея проста: використовуйте сітку на зовнішній стороні, щоб витягнути поля елементів за їх межі вдвічі менше внутрішньої сітки (використовуючи від’ємні поля), використовуйте внутрішню сітку для рівномірного розміщення елементів на потрібну кількість.

Оновлення: Деякі коментарі користувача сказали, що це не працює, ось короткий демонстраційний відеоролик: 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>

1
Моя помилка - я не помітив неявного стилю кнопки. Мене трохи відволікає негативна маржа.
15ee8f99-57ff-4f92-890c-b56153

-6

Іноді найпростіший метод - найкращий. Просто обклейте свої пасма з пробілами. Якщо це лише кілька текстових полів тощо, це далеко не найпростіший метод.

Ви також можете просто вставити порожні стовпці / рядки з фіксованим розміром. Надзвичайно простий, і ви можете легко змінити його.


Можливо, тому, що це не дуже вдалий метод. Використання пробілів може бути "простим виходом", але насправді це більше хак. Це не точно, він (і, ймовірно, може) рендерувати по-різному і ненадійно на моніторах з різним масштабуванням, і ви не маєте контролю над точним обсягом створеного вами простору. Вставлення порожніх стовпців / рядків призведе до безладу вашої сітки ...
Позначте
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.