无法找到正确的Dispatcher以防止“此类型的CollectionView不支持从线程更改...”异常

时间:2014-08-09 19:37:23

标签: wpf multithreading asynchronous

在下面的代码中,我无法从异步调用中更新WPF Window。所有显示的都是“第一项”。问题是Window不是从WPF应用程序启动的,而是从类模块启动的。由于我们的主UI应用程序是VB6应用程序,它通过调用Com visible Net dll来启动som WPF元素。这种方法效果很好,但下面不是这样。

主要控制台应用

Imports Window
Imports System.Windows
Imports System.Threading

Module Module1

    Sub Main()

        Dim launcher As New WindowLauncher
        launcher.LaunchWindow()
        Console.WriteLine("Press a key to continue...")
        Console.ReadLine()

    End Sub

End Module
WPF WINDOW LAUNCHER

Imports Window

Public Class WindowLauncher

    Public Sub LaunchWindow()
        Dim model As New ViewModel
        Dim window As New DisplayWindow
        model.Dispatcher = window.Dispatcher
        window.DataContext = model
        window.Show()

        model.Collection.Add("Second item.")

        model.StartAddingItems()
        'window.Close()
    End Sub

End Class
WPF用户控制图书馆项目中的WPF窗口及其视图模式

<Window x:Class="DisplayWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DisplayWindow" Height="300" Width="300">
    <Grid>
        <ListBox ItemsSource="{Binding Collection}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}"/>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

Imports System.Collections.ObjectModel
Imports System.Threading
Imports System.Windows.Threading

Public Class ViewModel

    Public Sub New()
        Collection = New ObservableCollection(Of String)
        Collection.Add("First item")
    End Sub

    Public Property Collection As ObservableCollection(Of String)

    Public Property Dispatcher As Dispatcher

    Public Sub StartAddingItems()
        Dim progress As New Progress(Of String)
        AddHandler progress.ProgressChanged, AddressOf ProgressChanged

        For i = 1 To 10
            DoSomething(progress).Wait()
        Next
    End Sub

    Private _counter As Integer
    Private Async Function DoSomething(progress As IProgress(Of String)) As Task
        Await Task.Delay(10 * _counter)
        _counter += 1
        progress.Report(CStr(_counter))
    End Function

    Private Sub ProgressChanged(sender As Object, e As Object)

        Dim text = CStr(e)
        Me.Dispatcher.BeginInvoke(Sub() AddToCollection(text), DispatcherPriority.Background)
        'The challenge seems to be to find the correct Dispatcher. Now the property is set from the WPF Window, but the code never reaches the AddToCollection method. `
        AddToCollection(text)
        `This call throws the well known "This type of CollectionView does not support changes..." exception.` 
    End Sub

    Private Sub AddToCollection(text As String)
        Collection.Add(String.Format("item {0}", text))
    End Sub

End Class

2 个答案:

答案 0 :(得分:0)

问题出在以下几个电话中:

model.Collection.Add("Second item.")
model.StartAddingItems()

这似乎阻止了对用户界面的更新。

我将这些调用移动到了窗口。WPF.Form的加载事件(表单保存了对viewmodel的引用),并将viewmodel&#39; s Dispatcher设置为那里的窗口。 / p>

答案 1 :(得分:0)

如果你正在显示WPF窗口,你真的需要一个单独的WPF线程(带有一个STA主循环):

var thread = new Thread(() =>
{
  var application = new Application();
  application.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();