Размещение элементов в контроле Grid может быть утомительным. Удобнее, когда они автоматом размещаются по ячейкам, как в UniformGrid. 
ColumnsGrid добавляет к этому следующие возможности:
Публикации на эту тему на Хабре:
> Фишки XAML-разработчика: динамический Grid
              
            ColumnsGrid добавляет к этому следующие возможности:
- Колонки задаются одной строкой Columns=«Auto,*,200,Shared1»
- Задается интервал между колонками Spacing=«4»
- Переход на следующую строку задается элементом <Separator Height=«4»/>
using System;
using System.Windows;
using System.Windows.Controls;
public class ColumnsGrid : Grid {
  public ColumnsGrid() : base() {
    ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
    RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
  }
Свойство Spacing
public double Spacing { 
  get { return (double)GetValue(SpacingProperty); } 
  set { SetValue(SpacingProperty, value); } 
}
public static readonly DependencyProperty SpacingProperty = 
  DependencyProperty.Register("Spacing", typeof(double), typeof(ColumnsGrid),
    new FrameworkPropertyMetadata(0.0, SpacingChangedCallback));
static void SpacingChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
  ((ColumnsGrid)d).SpacingChanged((double)e.NewValue); 
}
  void SpacingChanged(double value) {
    bool even = true;
    var l = new GridLength(Spacing);
    foreach (var cd in ColumnDefinitions) { 
      if (!even) cd.Width = l; 
      even = !even; 
    }
  }
Свойство Columns
public string Columns{ 
  get { return (string)GetValue(ColumnsProperty); } 
  set { SetValue(ColumnsProperty, value); } 
}
public static readonly DependencyProperty ColumnsProperty = 
  DependencyProperty.Register("Columns", typeof(string), typeof(ColumnsGrid),
    new FrameworkPropertyMetadata("Auto", ColumnsChangedCallback));
static void ColumnsChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e) { 
  ((ColumnsGrid)d).ColumnsChanged((double)e.NewValue); 
}
  void ColumnsChanged(string value) {
    var ss = value.Split(',');
    ColumnDefinitions.Clear();
    var cnv = new GridLengthConverter();
    foreach (var s in ss) {
      var cd = new ColumnDefinition();
      try {
        cd.Width = (GridLength)cnv.ConvertFromInvariantString(s.Trim());
      } catch {
        cd.Width = GridLength.Auto;
        cd.SharedSizeGroup = s;
      }
      if (ColumnDefinitions.Count>0) ColumnDefinitions.Add(
        new ColumnDefinition() { Width = new GridLength(Spacing) });
      ColumnDefinitions.Add(cd);
    }
    if (ColumnDefinitions.Count==0) ColumnDefinitions.Add(
      new ColumnDefinition() { Width = GridLength.Auto });
    InvokeArrange();
  }
  
  protected override void OnVisualChildrenChanged(DependencyObject visualAdded, DependencyObject visualRemoved) {
    base.OnVisualChildrenChanged(visualAdded, visualRemoved);
    InvokeArrange();
  }
  bool ArrangeInvoked;
  void InvokeArrange() {
    if (ArrangeInvoked) return;
    ArrangeInvoked = true;
    Application.Current.Dispatcher.BeginInvoke(new Action(Arrange));
  }
  void Arrange() {
    ArrangeInvoked = false;
    RowDefinitions.Clear();
    RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
    int row = 0, col = 0;
    foreach (UIElement child in InternalChildren) {
      if (child is Separator) {
        child.Visibility = Visibility.Hidden;
        col = 0;
        row++;
        RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
        SetColumn(child, 0);
        SetRow(child, row);
        row++;
        RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
      } else {
        SetColumn(child, col);
        SetRow(child, row);
        col += 2;
      }
    }
  }
}
Публикации на эту тему на Хабре:
> Фишки XAML-разработчика: динамический Grid
Комментарии (6)
 - geekmetwice26.08.2017 20:04+4- Отвратительная подача материала! Какой-то ColumnsGrid… он что, размещает контролы в колонки, а не по строкам? Пара фич + никчемушные листинги, зато ни строчки XAML-кода! Кто и как может извлечь пользу из вашего… гм… даже не опуса, а какого-то «текстового выкрика»?  - AmirYantimirov Автор27.08.2017 08:42- Пожалуйста: 
 - <local:ColumnsGrid Columns="Header,*" Spacing="4" > <RadioButton Name="radioFontDefault" Content="Default font" /> <TextBox Name="textFontDefault" Text="Segoe UI" IsReadOnly="True"/> <Separator Height="4" /> <RadioButton Name="radioFontCustom" Content="Custom font" /> <TextBox Name="textFontCustom" Text="Segoe UI" IsReadOnly="True"/> </local:ColumnsGrid>
  
 
 - lexxpavlov26.08.2017 23:41- Только вчера сделал такой же грид, с тем же названием. Только я отнаследовал от Panel, а не Grid (как и UniformGrid). У меня ещё есть RowSpacing, помимо ColumnSpacing. 
 
           
 
Nikita_Danilov
Надежный и точный Layout и так очень мощная сторона WPF, за это он мне полюбился в своё время.
Но, к сожалению, за все эти годы так и не подвезли возможность устанавливать несколько стилей для одного элемента, приходится плодить мелкие подстили на каждый случай, из-за чего становится невыносимой поддержка больших библиотек стилей.
kekekeks
А вот в Авалонии эту проблему устранили.