WPF数据网格绑定直到点击行标题才更新

15

我有一个DataGrid控件,它绑定了视图模型中的一个List。该控件的内容在我点击行标题之前不会更新。点击各种单元格对其没有影响,我必须点击标题。

以下是XAML中的DataGrid控件:

<DataGrid x:Name="TransactionDetailsGrid" Grid.Row="1" AutoGenerateColumns="False" SelectionMode="Extended" IsReadOnly="True" HeadersVisibility="Column"
                  ItemsSource="{Binding TransactionDetailList}" SelectedItem="{Binding SelectedTransactionDetail}" GridLinesVisibility="None">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Path=Account.AccountNumber}" Header="Account No." HeaderStyle="{StaticResource DataGridHeaderStyleCenter}" Width="120" />
        <DataGridTextColumn Binding="{Binding Path=Account.AccountName}" Header="Account Name" HeaderStyle="{StaticResource DataGridHeaderStyleCenter}" Width="*" />
        <DataGridTextColumn Binding="{Binding Path=Amount}"  Header="Amount" HeaderStyle="{StaticResource DataGridHeaderStyleCenter}" Width="120" />
    </DataGrid.Columns>
</DataGrid>

这是来自视图模型的内容:

public List<TransactionDetail> TransactionDetailList
{
    get { return this._transactionDetailList; }

    set
    {
        this._transactionDetailList = value;
        RaisePropertyChanged("TransactionDetailList");                
    }
}

这是视图模型中其中一项的编辑:

private void AddTransactionDetail()
{
    TransactionDetailViewModel viewModel = new TransactionDetailViewModel();

    MainWindowViewModel.ViewLoader.ShowDialog(viewModel);

    if (viewModel.TransactionDetail != null)
    {
        this.TransactionDetailList.Add(viewModel.TransactionDetail);
        RaisePropertyChanged("TransactionDetailList");
    }
}
在此运行后,我可以在TransactionDetailList的Getter上设置断点,并且集合中有该项。然而,数据表是空的。如果我点击标题行,项目将会显示在网格中。
当进行编辑时,我遇到了相同的问题。
我以前成功地做过这件事,所以我不确定这里有什么不同。我是否漏掉了一些明显的东西?为什么网格直到我点击标题栏才会显示其内容?
我刚刚注意到了一些有趣的事情。当我点击网格标题时,TransactionDetailList Getter中的断点没有被触发,但数据仍然显示出来。因此,就好像网格已经有了信息,只是在点击标题之前没有显示出来。
改用ObservableCollection后,它可以工作了。但现在我在编辑时也遇到了同样的问题(网格直到单击标题后才更新)。
private void EditTransactionDetail()
{
    TransactionDetailViewModel viewModel = new TransactionDetailViewModel(this.SelectedTransactionDetail);

    MainWindowViewModel.ViewLoader.ShowDialog(new TransactionDetailViewModel(this.SelectedTransactionDetail));

    RaisePropertyChanged("TransactionDetailList");
}

我的实体类需要实现 INotifyPropertyChanged 接口吗?如果我更改了集合并调用了 RaisePropertyChanged 方法,那不应该会导致网格更新吗?

2个回答

10
问题在于,当您从集合中添加或删除项时,不会调用setter。这意味着未调用INotifyPropertyChanged,视图无法知道需要刷新。
WPF通过同时支持INotifyCollectionChanged接口来解决此问题。
尝试使用ObservableCollection<TransactionDetail>而不是List<TransactionDetail>ObservableCollection<T> 是内置的,并实现了INotifyCollectionChanged,因此您不必大幅修改代码。
还要确保您继续在视图模型上实现INotifyPropertyChanged,就像您已经实现的那样。这样,当替换整个集合(调用setter时)时,视图将收到通知。
public ObservableCollection<TransactionDetail> TransactionDetailList
{
    get { return this._transactionDetailList; }

    set
    {
        this._transactionDetailList = value;
        RaisePropertyChanged("TransactionDetailList");                
    }
}

参见:

编辑:

此外,你也应该在TransactionDetail上实现INotifyPropertyChanged。如果不能,请将其包装在一个实现了INotifyPropertyChanged的类中。

如果你没有在TransactionDetail上实现它,那么你所做的更改不会影响列表,但会影响TransactionDetail实例上的属性,在UI中不会显示,直到你以某种方式刷新整个列表。

如果你试图通过调用列表属性上的RaisePropertyChanged来修复这个问题,那么你的UI会认为整个列表(以及整个UI对象集)需要被丢弃并更新。这会降低性能并使应用程序变得迟缓。


那么,当我更改列表中的项目时,这行代码是否应该导致数据网格更新,以显示其中更新后的项目? RaisePropertyChanged("TransactionDetailList"); - Bob Horn
1
@Bob:不对。你的setter的工作方式与语言中的任何其他属性setter相同。它仅在替换整个“TransactionDetailList”时才会被调用。在UI的情况下,这并不是非常有帮助,这正是为什么他们创建了“INotifyCollectionChanged”和“ObservableCollection<T>”的原因所在。 - Merlyn Morgan-Graham
1
@Bob:哦,等等,你是指你问题中的最后一个吗?不,当你只改变列表中一个项目的一个属性的值时,你不应该告诉UI整个列表正在改变。这会完全影响性能。相反,在TransactionDetail类上也实现INotifyPropertyChanged。 - Merlyn Morgan-Graham
但是,如果调用了setter,这会有影响吗?如果我明确地调用RaisePropertyChanged("TransactionDetailList"),那么这不应该导致绑定更新吗? - Bob Horn
好的,这是我现在尝试的:在TransactionDetail类上实现INotifyPropertyChanged。感谢您的帮助。我非常感激。 - Bob Horn
显示剩余2条评论

2
如果您希望当您更改绑定的IEnumerable列表时,网格会立即更新,请将其作为实现了INotifyCollectionChanged接口的列表类型。一个内置数据类型具有此功能,即ObservableCollection也在这里)。因此,如果您使您的属性如下所示:
public ObservableCollection<TransactionDetail> TransactionDetailList
{
    get { return this._transactionDetailList; }

    set
    {
        this._transactionDetailList = value;
        RaisePropertyChanged("TransactionDetailList");                
    }
}

您的网格将自动获取您添加或删除列表中的任何项目。
实际上,您在这里所做的是通知列表何时发生了更改(即列表引用),但您没有在列表内容发生更改时进行通知。

我改用了ObservableCollection,现在表格正在更新。以前我只是使用List<T>,也可以实现这个功能。 - Bob Horn
1
@Bob:你可能调用了setter并替换了整个列表。要么是这样,要么是在查询列表之前进行了更新。 - Merlyn Morgan-Graham

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