这是一个WPF Datagrid的bug吗?

5
我有以下的概念证明:
XAML 窗口:
<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>

代码后台:

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; }
}
}

执行应用程序,取消选中顶部的一些框,向下滚动,再次更改一些框并向上滚动。看,复选框又被选中了!
我是不是漏掉了什么,还是应该向 Microsoft 提交错误报告?
编辑:感谢您的回复,但与 INotify 或 Checkbox 无关,使用 TextBox 和 INotify 也会发生同样的情况。 您甚至无需在滚动后单击复选框,只需取消选中一些框,向下滚动,向上滚动即可。 检查此代码:
<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>

代码如下:

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
}
}

1
如果让Data继承自INotifyPropertyChanged并使用属性更改通知,您的结果会发生变化吗? - Rachel
尝试关闭回收。是否调用了设置?如果没有,请使用UpdateSourceTrigger="PropertyChanged"。像Rachel所说的那样,使用INotify。 - paparazzo
1
我会按照Rachel的建议尝试将EnableRowVirtualization="false"。 - Xcalibur37
@Xcalibur37 就是这样!将 EnableRowVirtualization 设置为 false 后,网格就能按预期工作了!这是虚拟化功能的一个 bug 吗? - Ignacio Soler Garcia
在虚拟化回收方面搜索msnd:http://msdn.microsoft.com/en-us/library/system.windows.controls.virtualizationmode.aspx - paparazzo
显示剩余4条评论
2个回答

6
这个问题与回收无关。事实上,禁用回收会隐藏真正的问题:您的 Data 对象属性从未更新。尝试在 EnabledText setter 中设置断点,您会发现更改文本或选中/取消复选框时不会发生任何事情。当您滚动到其他位置再返回时,属性将从对象重新读取,由于它没有更改,复选框正确地更新以匹配 Enabled 属性。 DataGrid 的默认状态应为所有行都处于显示模式,用户需要时才切换到当前选择的行的编辑模式。直到用户验证整行后,值才被提交。
在幕后,为整个行创建了一个隐式的BindingGroup,有效地将所有绑定设置为UpdateSourceTrigger.Explicit。当用户完成编辑行时,此绑定组被提交。在您的情况下,由于没有BeginEdit,因此不会有任何CommitEdit,值永远不会更新。
您有几个解决方案:
  • 使用另一个不具有“切换到编辑模式”行为的控件,例如ListView
  • 强制每个绑定的UpdateSourceTriggerPropertyChangedLostFocus
  • 更改用户使用网格的方式以符合DataGrid的行为。

1

最后我在微软提交了一个缺陷,因为无论是否使用VirtualRows,这都不是预期的工作方式。

缺陷报告在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接