To access properties via data binding that are not normally reachable, like the
Columns.Count
property of the
DataGrid
control, you can write a wrapper around the control and add a custom property.
public class MyDataGrid : DataGrid, IDisposable
{
private const int DefaultColumnCountValue = 0;
private static readonly Type ctrlType = typeof(MyDataGrid);
private const string ctrlName = nameof(MyDataGrid);
public static readonly DependencyProperty ColumnCountProperty =
DependencyProperty.Register(nameof(ColumnCount),
typeof(int),
ctrlType,
new PropertyMetadata(DefaultColumnCountValue,
OnColumnCountChanged));
[Bindable(true)]
[Description("Column Count"), Category(ctrlName)]
public int ColumnCount
{
get => (int)GetValue(ColumnCountProperty);
set => SetValue(ColumnCountProperty, value);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.Columns.CollectionChanged += Columns_CollectionChanged;
}
private void Columns_CollectionChanged(
object? sender,
NotifyCollectionChangedEventArgs e)
=> ColumnCount = this.Columns.Count;
private static void OnColumnCountChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
}
public void Dispose()
=> this.Columns.CollectionChanged -= Columns_CollectionChanged;
}
Now we can bind to the property:
<local:MyDataGrid x:Name="MyDataGrid"
ColumnCount="{Binding ColumnCount, Mode=OneWayToSource}"
ItemsSource="{Binding Items}" />
Here is the complete demo:
1.
MainWindow
Xaml
<Window x:Class="WpfDataGridCountHelper.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:WpfDataGridCountHelper"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<local:MyDataGrid x:Name="MyDataGrid"
ColumnCount="{Binding ColumnCount, Mode=OneWayToSource}"
ItemsSource="{Binding Items}" />
<Grid Grid.Row="1"
Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="DarkGray"
BorderThickness="2"
Padding="20 10">
<TextBlock Margin="0 10" HorizontalAlignment="Stretch">
<Run FontWeight="Bold" Text="Column Count: "/>
<Run Text="{Binding ColumnCount}"/>
</TextBlock>
</Border>
<Button Grid.Column="1"
Margin="10 0 0 0"
Padding="20 10"
Content="ADD COLUMN"
Click="ButtonBase_OnClick"/>
</Grid>
</Grid>
</Window>
2.
MainWindow
code-behind for the button click event (demo purposes only):
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
=> MyDataGrid.Columns.Add(new DataGridTextColumn());
}
3.
MainViewModel
+
ObservableObject
(
INotifyPropertyChanged
Handler class)
public abstract class ObservableObject : INotifyPropertyChanged
{
public void Set<TValue>(
ref TValue field,
TValue newValue,
[CallerMemberName] string propertyName = "")
{
if (!EqualityComparer<TValue>.Default.Equals(field, default)
&& field!.Equals(newValue))
return;
field = newValue;
PropertyChanged?.Invoke(this,
new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler? PropertyChanged;
}
public class MainViewModel : ObservableObject
{
private int columnCount = 0;
public int ColumnCount
{
get => columnCount;
set => Set(ref columnCount, value);
}
public ObservableCollection<PersonModel> Items { get; set; } = new()
{
new() { Name = "Freddie", Age = 21 },
new() { Name = "Milly", Age = 18 },
new() { Name = "Caddie", Age = 23 },
};
}
public class PersonModel : ObservableObject
{
private string? name;
private int? age;
public string? Name
{
get => name;
set => Set(ref name, value);
}
public int? Age
{
get => age;
set => Set(ref age, value);
}
}
Clicking on the
ADD COLUMN
button adds additional blank columns to the
MyDataGrid
control, then binding triggers and the
TextBlock
in the footer updates with the
ColumnCount
, all bound via the
DataContext
(
MainViewModel
).
Enjoy!