Search code examples
c#wpfbindingdatagriddatagridcolumnheader

DataGrid columns do not refresh values with DataGrid2D


I am trying to bind columns and rows of DataGrid to the same source but DataGrid does not show new value in the ColumnHeader when I change value in the RowHeader after TextBox lost focus. Headers collection contains new values as expected.

xmlns:dataGrid2D="http://gu.se/DataGrid2D"

<Grid>
    <DataGrid
        dataGrid2D:ItemsSource.RowHeadersSource="{Binding Headers}"
        dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding Headers}"
        dataGrid2D:ItemsSource.Array2D="{Binding Items}"
        IsReadOnly="True"
        AutoGenerateColumns="True">
        <DataGrid.RowHeaderTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Value}" />
            </DataTemplate>
        </DataGrid.RowHeaderTemplate>
    </DataGrid>
</Grid>

DataContext:

public class MainViewModel
{
    public IEnumerable<Header> Headers { get; } = new Header[]
    { new Header { Value = "1" }, new Header { Value = "2" } };
    public string[,] Items { get; } = new string[2, 2] { { "1", "2" }, { "3", "4" } };
}
public class Header
{
    public string Value { get; set; }

    public override string ToString()
    {
        return Value;
    }
}

What I am doing wrong?

I am changing the Value property from the View side so it does not need INotifyPropertyChanged interface. If I call "Refresh" on DataGrid it updates but with blinking.

Edit:

Why are you downvoting me? Is it a bad question?


Solution

  • Here is one possible solution using the style for DataGridColumnHeader. I also explicitly used the Value property instead of the ToString method.

    <Grid>
        <DataGrid
        dataGrid2D:ItemsSource.RowHeadersSource="{Binding Headers}"
        dataGrid2D:ItemsSource.ColumnHeadersSource="{Binding Headers}"
        dataGrid2D:ItemsSource.Array2D="{Binding Items}"
        IsReadOnly="True"
        AutoGenerateColumns="True">
            <DataGrid.Resources>
                <Style TargetType="{x:Type DataGridColumnHeader}">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate>
                                <TextBlock Text="{Binding Value}"/>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DataGrid.Resources>
            <DataGrid.RowHeaderTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
                </DataTemplate>
            </DataGrid.RowHeaderTemplate>
        </DataGrid>
    </Grid>
    

    You need to slightly change the ModelView then:

        public class Header: INotifyPropertyChanged
        {
            private string _Value;
    
            public string Value
            {
                get
                {
                    return _Value;
                }
                set
                {
                    _Value = value;
                    OnPropertyChanged("Value");
                }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            public void OnPropertyChanged(string name)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }
        }