【问题标题】:"An ItemsControl is inconsistent with its items source" thrown when calling MessageBox.Show()调用 MessageBox.Show() 时抛出“一个 ItemsControl 与其项目源不一致”
【发布时间】:2015-11-08 10:46:59
【问题描述】:

我对 WPF 非常陌生,遇到以下问题:

我正在尝试开发一个练习应用程序来帮助我控制我的预算。

我有一个像这样的 Partida 类:

public class Partida
{
    public delegate void PartidaChangedHandler(Partida p);
    public event PartidaChangedHandler OnPartidaChanged;
    private ObservableCollection<PartidaEntry> content;

    public Partida()
    {
        content = new ObservableCollection<PartidaEntry>();
        content.CollectionChanged += PartidaEntriesCollectionChanged;

    }

    public void PartidaEntriesCollectionChanged(object s, NotifyCollectionChangedEventArgs args)
    {
        if (OnPartidaChanged != null)
        {
            OnPartidaChanged(this);
        }
    }
}

我正在使用 Datagrid 显示 content 集合,但我需要知道此类的 content 集合何时更改并触发 OnPartidaChanged 事件。

在课堂之外(在 MainWindow 中)我连接到 OnPartidaChanged 事件,如下所示:

p.OnPartidaChanged += (Partida ppp) =>
{
    int foo = 5;
    MessageBox.Show("A partida has changed!");
};

当我在 DataGrid 中添加新行时,事件会正确触发,但是一旦执行 MessageBox,我就会收到带有以下消息的 InvalidOperationException

ItemsControl 与其项目源不一致。

知道如何解决这个问题而不失去监听 ObservableCollection 的 CollectionChanged 事件并在该事件之后触发 OnPartidaChanged 的​​能力吗?

提前致谢!

P.S.:另外,我想知道 MessageBox 与 ItemControl 到底有什么关系……如果 MessageBox 只显示一个简单的框,为什么会触发异常! :S

【问题讨论】:

  • 您可以发布您的 ItemsControl 的 xaml 吗?
  • 我在这里发现了另一个与完全相同的问题 (stackoverflow.com/questions/19120504/…) 的问题。选择的答案只是删除 MessageBox.Show(),实际上他们说删除整个 CollectionChanged 事件处理程序。但是实际上并不能解决问题!
  • @FjjF 我的链接答案如何不能解决您的问题?删除MessageBox.Show() 调用后是否仍会出现异常?你能澄清一下吗?

标签: c# wpf


【解决方案1】:

WPF add item to datagrid bound to observablecollection exception 中的答案清楚地解释了正在发生的事情:在更改集合的操作正在进行时调用事件处理程序,调用MessageBox.Show() 使调度程序消息处理循环有机会启动再次处理消息。这会导致与 WPF 工作方式不兼容的重入:集合更改操作尚未完全解决,但 UI 有机会尝试运行不应运行的逻辑,直到 after 该操作已完全解决。

换句话说,就像异常状态一样,控件处于不一致状态,因为它被允许执行一些不应该发生的处理,直到它完全处理完集合更改。

我承认另一个答案中的建议并不是非常有说服力。就其而言,这是合理的建议,但没有提供真正的替代方案。

在您的场景中,在不更改您的实现的任何其他内容的情况下,一个明显的解决方案是将消息框推迟到完全解决集合更改操作之后。您可以使用Dispatcher.InvokeAsync() 方法将调用的执行推迟到MessageBox.Show()

p.OnPartidaChanged += (Partida ppp) =>
{
    int foo = 5;
    Dispatcher.InvokeAsync(() => MessageBox.Show("A partida has changed!"));
};

当然,问题在于显示消息框是否真的是处理事件的最佳方式。从您问题中的有限信息中并不清楚为什么您的事件处理程序看起来如此。如果您确定每次集合更改时都显示一个消息框确实是正确的做法,那么上述应该可以解决您的问题。

但是您可能需要考虑将信息呈现给用户的其他方法,例如在 UI 的状态字段中显示它,或者甚至提供某种事件日志,例如在多行文本框或列表框中。这些方法通常会涉及数据绑定,这些数据绑定适合 WPF 中的正常事件流和数据处理,并且可以作为同步代码工作,而不会遇到您在此处看到的问题。

【讨论】:

    猜你喜欢
    • 2013-10-25
    • 1970-01-01
    • 2011-10-08
    • 2022-08-22
    • 2021-04-13
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 2020-08-04
    相关资源
    最近更新 更多