Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a WPF Datagrid bug? [closed]

Tags:

.net

wpf

datagrid

I have the following proof of concept:

XAML window:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">

<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" >
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Name}" />
        <DataGridTemplateColumn >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <CheckBox IsChecked="{Binding Mode=TwoWay, Path=Enabled}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
</Window>

Code behind:

using System.Collections.ObjectModel;
using System.Windows;

namespace WpfApplication1
{
public partial class MainWindow : Window
{
    public ObservableCollection<Data> Items { get; private set; }

    public MainWindow()
    {
        InitializeComponent();
        this.Items = new ObservableCollection<Data>();
        this.DataContext = this;
        for (int index = 0; index < 30; index++)
        {
            this.Items.Add(new Data() {Enabled = true });   
        }
    }
}

public class Data
{
    public bool Enabled { get; set; }
}
}

Execute the app, uncheck some boxes at the top, scroll down, change some boxes again and scroll up. Voilá, the checkboxes are checked again!

Am I missing something or should I fill a bug to Microsoft?

EDIT: Thanks for your responses but it's not related with the INotify or the Checkbox, with a TextBox and the INotify the same happens. You don't event need to click the checkboxes after scrolling, just uncheck some, scroll down, scroll up and voila, they are checked again. Check this code:

<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False" >
    <DataGrid.Columns>
        <DataGridTemplateColumn >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal">
                        <CheckBox IsChecked="{Binding Mode=TwoWay, Path=Enabled}" />
                        <TextBox Text="{Binding Text}" />
                    </StackPanel>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>
</Window>

And the code behind:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace WpfApplication1
{
public partial class MainWindow : Window
{
    public ObservableCollection<Data> Items { get; private set; }

    public MainWindow()
    {
        InitializeComponent();
        this.Items = new ObservableCollection<Data>();
        this.DataContext = this;
        for (int index = 0; index < 30; index++)
        {
            this.Items.Add(new Data() { Enabled = true, Text = index.ToString() });
        }
    }
}

public class Data : INotifyPropertyChanged
{
    private bool _enabled;
    public bool Enabled
    {
        get { return _enabled; }
        set
        {
            if (value != _enabled)
            {
                _enabled = value;
                this.OnPropertyChanged("Enabled");
            }
        }
    }

    private string _text;
    public string Text
    {
        get { return _text; }
        set
        {
            if (value != _text)
            {
                _text = value;
                this.OnPropertyChanged("Text");
            }
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string name)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }

    #endregion
}
}
like image 243
Ignacio Soler Garcia Avatar asked Jul 19 '12 17:07

Ignacio Soler Garcia


2 Answers

This problem isn't related to recycling. In fact, disabling recycling hides the real problem: your Data object properties are never updated. Try to set a breakpoint in the Enabled or Text setter and you'll see that nothing will happen when you change the text or check/unchecked the box. When you scroll away and back, the property is reread from the object, and since it hasn't changed, the checkbox is correctly updated to match the Enabled property.

A DataGrid is meant to have all rows in display mode by default, with the user switching to editing mode on the currently selected row when he needs to. The values aren't committed until the user validates the whole row.

Behind the scenes, an implicit BindingGroup is created for the whole row, effectively setting all the bindings to UpdateSourceTrigger.Explicit. This binding group is committed when the user is done editing the row. In your case, since there is no BeginEdit, there won't be any CommitEdit and the values won't ever be updated.

You have several solutions here:

  • Use another control that doesn't have this "switch to edit mode" behavior, such as a ListView.
  • Force the UpdateSourceTrigger to PropertyChanged or LostFocus on every binding.
  • Change how the user uses the grid to comply with the DataGrid behavior.
like image 56
Julien Lebosquain Avatar answered Nov 05 '22 06:11

Julien Lebosquain


Finally I've entered a defect at Microsoft because this is not the expected way of working wherever or not VirtualRows are used.

Bug report here

like image 30
Ignacio Soler Garcia Avatar answered Nov 05 '22 08:11

Ignacio Soler Garcia