Click here to Skip to main content
15,880,796 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
How can I dynamically get the column count of a WPF datagrid inside a viewmodel method following mvvm ?

What I have tried:

Essentially datagrid.columns.count to a int variable but using mvvm.
Posted
Updated 29-Dec-22 16:32pm

1 solution

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.
C#
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();

        // listen for changes
        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)
    {
        // can do something here when the property value is changed
    }

    public void Dispose()
        => this.Columns.CollectionChanged -= Columns_CollectionChanged;
}

Now we can bind to the property:
XML
<local:MyDataGrid x:Name="MyDataGrid"
    ColumnCount="{Binding ColumnCount, Mode=OneWayToSource}"
    ItemsSource="{Binding Items}" />

Here is the complete demo:

1. MainWindow Xaml
XML
<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):
C#
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)
C#
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!
 
Share this answer
 
v2
Comments
Member 12692000 29-Dec-22 23:17pm    
@graeme_grant But isn't using the click event i.e. Click="ButtonBase_OnClick" and then
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        => MyDataGrid.Columns.Add(new DataGridTextColumn());

in your code break the mvvm pattern ? Also where do I put the code of the class MyDataGrid ?
Graeme_Grant 29-Dec-22 23:42pm    
It was not part of the question, that is why I labeled it "(demo purposes only)" - it is there just to demonstrate the data binding was working.

I did however answer your MVVM Question: "How do I get datagrid.columns.count using MVVM C#"".

[edit] Here are a number of solutions for you to choose from: mvvm c# wpf add columns to datagrid - Google Search[^]
Member 12692000 29-Dec-22 23:56pm    
My bad, I missed the demo purposes only part. Let me test it properly. Thanks!
Graeme_Grant 30-Dec-22 0:05am    
All good. The code posted is a working version - it was put together for this question.
Member 12692000 31-Dec-22 8:47am    
I have never used wrapper around any control and add a custom property to it before so maybe I'm doing something wrong here because I'm getting the column count as 0. My datagrid looks like
<local:MyDataGrid 
										ColumnCount="{Binding ColumnCount, Mode=OneWayToSource}"
										ItemsSource="{Binding Students}"
										SelectedItem="{Binding SelectedStudent}"
										CanUserAddRows="False"
										CanUserDeleteRows="False"
										SelectionMode="Single"
										SelectedIndex="{Binding SelectedIndex}"
										AutoGenerateColumns="False"
										HeadersVisibility="Column"
                                    <DataGrid.Columns>
                                            <DataGridTextColumn
												Header="Vendor"
												Binding="{Binding Path=Name, Mode=OneWay}"
												Width="350"
												IsReadOnly="True" />
                                            <DataGridTextColumn
												Header="CNDN No."
												Binding="{Binding Path=Age, Mode=OneWay}"
												Width="320"
												IsReadOnly="True" />
											...
                                    </DataGrid.Columns>
                                    <DataGrid.Resources>
                                            <Style
												BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"
												TargetType="{x:Type DataGridColumnHeader}">
                                                <Setter
													Property="Background"
													Value="#AA00FF" />
                                                <Setter
													Property="FontSize"
													Value="15" />
                                                <Setter
													Property="FontWeight"
													Value="DemiBold" />
                                            </Style>
                                            <Style
												TargetType="{x:Type DataGrid}">
                                                <Setter
													Property="ColumnWidth"
													Value="*"></Setter>
                                                <Setter
													Property="FontSize"
													Value="13"></Setter>
                                            </Style>
                                            <!--Selected row color-->
                                            <Style
												TargetType="{x:Type DataGridCell}">
                                                <Style.Triggers>
                                                    <Trigger
														Property="DataGridCell.IsSelected"
														Value="True">
                                                        <Setter
															Property="Background"
															Value="#7CBA52" />
                                                        <Setter
															Property="Foreground"
															Value="#601BD1" />
                                                        <Setter
															Property="FontWeight"
															Value="DemiBold" />
                                                    </Trigger>
                                                    <Trigger
														Property="IsSelected"
														Value="True">
                                                        <Setter
															Property="BorderThickness"
															Value="0" />
                                                    </Trigger>
                                                </Style.Triggers>
                                                <EventSetter
													Event="PreviewMouseLeftButtonDown"
													Handler="DoSelectedRow" />
                                            </Style>
                                    </DataGrid.Resources>
</local:MyDataGrid>

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900