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
}
}
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:
ListView
.UpdateSourceTrigger
to PropertyChanged
or LostFocus
on every binding.DataGrid
behavior.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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With