Search code examples
c#wpfdata-bindingdatagrid

WPF Code-Behind data binding with AutoGeneratedColumns not working


I am trying to achieve rather something simple, and I believe that my approach might be wrong. I am creating a datagrid, where the first column has a seperate width from the other ones. I am using AutoGenerateColumns=true, as it simplifies my work. I cannot use pure XAML as I do not know the amount of columns before runtime, and I was not able to connect XAML and AutoGenerateColumns, so it would use the first column's layout, and then generate the rest.

My approaches:

1) Create two data grids next to each other - the issue with that approach is the need to manage 2 seperate datagrids, I saw issues with scrolling and adjusting their sizes, so I decided to change my approach, to keep everyhting within one DataGrid as the data relates to each other.

2) Trying to get the Datagrid object from Code-Behind so I can set the Width property from the ViewModel class, this would break the MVVM model, and also was difficult for my to implement

3) Current approach - using the AutoGeneratingColumn event, I capture the first column and try to bind to its WidthProperties. Unfortunately this does not seem to work, and I do not know why.

This is my Code-Behind file for the XAML containing the DataGrid

 private void DG1_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            string headername = e.Column.Header.ToString();
            //Cancel the column you don't want to generate
            if (headername == "DATE")
            {
                Binding binding = new Binding("DateColumnWidth");
                binding.Source = DataGrid.DataContext; // DataGrid is the name of the DataGrid
                binding.Mode = BindingMode.TwoWay;

                binding.Path = new PropertyPath("DateColumnWidth");
                BindingOperations.SetBinding(e.Column, ColumnDefinition.MinWidthProperty, binding);
                BindingOperations.SetBinding(e.Column, ColumnDefinition.MaxWidthProperty, binding);
                e.Column.Header = "Test";
            }
        }

This is my Proprty in the ViewModel. Whilst debugging the binding source, it attaches to the right class and I see all my properties. It also changes the header of the right column.


 private int _DateColumnWidth;

        public int DateColumnWidth
        {
            get { return _DateColumnWidth; }
            set
            {
                _DateColumnWidth = value;
                RaisePropertyChanged("DateColumnWidth");
            }
        }

I set the debugger to show me all the data binding tracing information, no problems arise, but the width is not updating. What am I doing wrong?


Solution

  • I created a mock up based on your code and it worked. Then I looked more closely and realised you have this:

    BindingOperations.SetBinding(e.Column, ColumnDefinition.MinWidthProperty, binding);
    

    ColumnDefinition is the wrong object. It should be DataGridColumn:

    BindingOperations.SetBinding(e.Column, DataGridColumn.MaxWidthProperty, binding);
    

    Here is my test. The grid's first column is bound to a ColumnWidth property on ViewModel. There is a Slider control below the grid with the same binding. Sliding the slider changes the first column's width.

    enter image description here

    MainWindow.xaml:

    <Window x:Class="SO_59604847_DataGridBoundColumnWidth.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:SO_59604847_DataGridBoundColumnWidth"
            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <local:ViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <DataGrid
                AutoGenerateColumns="True"
                ItemsSource="{Binding GridItems}"
                AutoGeneratingColumn="DataGrid_AutoGeneratingColumn">
            </DataGrid>
            <Slider Grid.Row="1" Minimum="1" Maximum="1000" Value="{Binding ColumnWidth}" />
        </Grid>
    </Window>
    

    MainWindow.xaml.cs:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        private void DataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            if (e.Column.Header.ToString() == "ColumnOne")
            {
                var binding = new Binding("ColumnWidth");
                binding.Source = this.DataContext;
                BindingOperations.SetBinding(e.Column, DataGridColumn.MinWidthProperty, binding);
                BindingOperations.SetBinding(e.Column, DataGridColumn.MaxWidthProperty, binding);
            }
        }
    }
    

    ViewModel.cs:

    public class ViewModel : INotifyPropertyChanged
    {
        private double _columnWidth;
        public double ColumnWidth 
        {
            get { return _columnWidth; }
            set { _columnWidth = value; FirePropertyChanged(); }
        }
    
        private List<GridItem> _gridItems = new List<GridItem>()
            {
                new GridItem() { ColumnOne = "1.1", ColumnTwo = "1.2", ColumnThree = "1.3" },
                new GridItem() { ColumnOne = "2.1", ColumnTwo = "2.2", ColumnThree = "2.3" },
                new GridItem() { ColumnOne = "3.1", ColumnTwo = "3.2", ColumnThree = "3.3" }
            };
    
        public List<GridItem> GridItems 
        { 
            get { return _gridItems; }
        }
    
        private void FirePropertyChanged([CallerMemberName] string caller = "")
        {
            PropertyChanged(this, new PropertyChangedEventArgs(caller));
        }
    
        public event PropertyChangedEventHandler PropertyChanged;
    }
    
    public class GridItem
    {
        public string ColumnOne { get; set; }
        public string ColumnTwo { get; set; }
        public string ColumnThree { get; set; }
    }
    

    It does not do two-way binding because that doesn't make sense if you're binding to the Min and Max column widths (the user can't change these - and indeed cannot change the width because the Min and Max widths are set to the same value). If your intention was to bind the width two-ways then you will need to bind to the WidthProperty dependency property and not the Min/MaxWidthProperty DPs (though, you may need a value converter then because Width is a GridLength not a plain number. Say so if that is what you were trying to do and I'll see if I can update the answer accordingly.