我是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
答案 0 :(得分:3)
WPF add item to datagrid bound to observablecollection exception中的答案清楚地解释了发生了什么:在正在更改集合的操作正在进行时调用事件处理程序,并且调用MessageBox.Show()
给出调度程序消息处理循环有机会再次开始处理消息。这会导致与WPF工作方式不兼容的重入:收集更改操作尚未完全解决,但是UI有机会尝试运行不应该运行的逻辑,直到之后< / em>该操作已完全解决。
换句话说,正如异常所述,控件处于不一致状态,因为它允许执行一些不应该发生的处理,直到它完全处理集合更改之后才会发生。
我承认其他答案中的建议并不十分引人注目。这是合理的建议,但没有提供真正的替代方案。
在您的方案中,如果不更改有关您的实现的任何其他内容,一个明显的解决方案是将消息框推迟到完全解决了收集更改操作之后。您可以使用Dispatcher.InvokeAsync()
方法将执行调用推迟到MessageBox.Show()
来执行此操作:
p.OnPartidaChanged += (Partida ppp) =>
{
int foo = 5;
Dispatcher.InvokeAsync(() => MessageBox.Show("A partida has changed!"));
};
当然,还有一个问题是,显示消息框是否真的是处理事件的最佳方式。从您的问题中的有限信息中可以清楚地看出为什么您的事件处理程序看起来如此。如果您确定每次收集更改时都显示一个消息框,那么上面应该可以解决您的问题。
但是您可能想要考虑向用户呈现信息的替代方法,例如在UI中的状态字段中显示信息,或者甚至提供某种事件日志,例如在多行文本框或列表框中。这些方法通常涉及数据绑定,这些数据绑定适合WPF中的正常事件流和数据处理,并且可以作为同步代码工作,而不会遇到您在此处看到的问题。