我在WPF应用中遇到了ComboBox。它与其他一些问题类似,但这个问题的经典解决方案似乎并没有起作用。
基本上,它与此问题相同:
WPF ComboBox SelectedItem Set to Null on TabControl Switch
但是,我的ItemsSource已经在SelectedItem之后的XAML中,这通常是对它进行排序。
发生的事情是我有一个带有组合框的视图,数据已经加载,然后一个事件被触发,更新了提供给ComboBox的数据。 ViewModel使用事件(由获取数据的BackgroundWorker触发)并更新其ObservableCollection,即带有新数据的ItemsSource。像这样:
int id = (int)Invoice.Customer.DatabaseID;
Customers = new ObservableCollection<Customer>(customers);
Invoice.Customer = Customers.FirstOrDefault(x => x.DatabaseID == id);
正如您所看到的,它会尝试将发票上的客户设置回原来的状态。这确实发生了,但是,在断点处观察到,但是,一旦完成,Customer就会从未识别的源中恢复为null(我的代码中没有一个出现在调用堆栈中,它是所有框架内容)。
ComboBox的XAML是:
<ComboBox DisplayMemberPath="AccountCode"
SelectedItem="{Binding Invoice.Customer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
ItemsSource="{Binding Customers}"/>
总而言之,我的ComboBox SelectedItem在ItemsSource更新后设置为null,并确保ItemsSource在SelectedItem不执行任何操作之后。我真的无法弄清楚为什么它被设置为空,我不确定在哪里看。我可以看到任何指针或事物,以便找到解决方案,这将非常感激。
编辑:好的,我已经玩了一些,我怀疑它与来自BackgroundWorker的更新有关。我在数据服务中使用Timer和BackgroundWorker定期更新数据库中的客户列表,以确保数据相对最新。 BackgroundWorker在完成时触发事件,以通知感兴趣的对象列表已更新。这似乎意味着当事件被消耗时,它们处于不同的线程中。当它以这种方式更新时,我将SelectedItem设置为正确的项后将其设置为null,因此将Invoice.Customer设置为null。我在我的视图中快速添加了一个按钮来更新客户而不使用BackgroundWorker,这似乎每次都有效。我想定期更新数据,但我需要先解决这个问题。
答案 0 :(得分:0)
好吧,正如我在编辑中所怀疑的那样,这与某些方式的线程有关。在更新被计时器启动后更新ComboBox ItemsSource,导致它在更新为正确的客户后设置为null。我在一个新的应用程序中确认了这种行为,它没有包含其中的所有其他内容,因此我可能会将其设置为null,我并不意味着(即使调用堆栈似乎强烈建议它我不这样做。)当事件被触发以更新结果时,与真实应用程序相比,我在新应用程序中获得了完全相同的调用堆栈。
在玩了一些不同的东西之后,我已经遇到过这种方法(在我的应用程序中 - 没有将它部署到真正的应用程序中,但是手指越过它也可以在那里工作!)就是让事件发生了通过TaskFactory进行竞争更新(来自Is it wrong to use the Dispatcher within my ViewModel?的想法)。
在ViewModel中声明一个TaskFactory:
TaskFactory uiFactory;
在你的构造函数中设置如下:
uiFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext());
然后,如果在数据更新后运行,请执行以下操作:
private void AsyncMethods_TaskCompleted(object sender, EventArgs e)
{
uiFactory.StartNew( () => UpdateResults());
}
此上下文中的UpdateResults与更新发票上的客户相同。它获取旧id,将ItemsSource设置为新集合,然后将绑定到SelectedItem的属性设置为新集合中的等效项。这似乎是有效的,并没有给我以前的奇怪的行为。我将它部署到实际的应用程序,并希望它也在那里工作。如果确实如此,我会回来接受这个答案。
答案 1 :(得分:-1)
有时当你创造一个新的&#39;一个Object的实例,它可以破坏你的绑定。您可以更新现有的收藏集,而无需拨打新的&#39;或者,您可以使ObservableCollection成为依赖项属性。
答案 2 :(得分:-1)
问题是由这两行引起的。
Customers = new ObservableCollection<Customer>(customers);
Invoice.Customer = Customers.FirstOrDefault(x => x.DatabaseID == id);
您的组合框源Customers
,您再次初始化它。然后,您尝试从新初始化的成员中获取数据。新初始化的成员将没有数据。
即Customers
中没有数据。因此Invoice.Customer
可能为空。
我不明白为什么要对其进行初始化,只是尝试从中获取数据。你跳过来填补资料来源吗?
如果您错过了填充源,请先填充数据源。然后,您可以运行此代码而无需再次初始化它,以便Invoice.Customer
不为空。
Invoice.Customer = Customers.FirstOrDefault(x => x.DatabaseID == id);