2017-09-27 44 views
-2

当我尝试更新一个ObservableCollection,我在一个单独的线程中使用我的XAML然后ui线程,我得到一个XamlParseException,它说,必须创建相同的线程上的DependencySource DependencyObject。我使用Caliurn Micro将ViewModel绑定到视图。WPF更新从异步任务抛出ObservableList XamlParseException

我尝试了几种方法来达到我的目标,下面的方法似乎是对我来说最合理的方法。我将UI中的SyncronizationContext传递给任务,以便在执行繁重的工作负载后更新UI。

我在做什么错了?

查看

<Window x:Class="QuickScope.Views.NavigatorView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:QuickScope.Views" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    xmlns:quickScope="clr-namespace:QuickScope" 
    mc:Ignorable="d"> 
<Grid> 
    <TextBox Grid.Row="0" 
      Name="TextBox" 
      HorizontalContentAlignment="Stretch" 
      VerticalContentAlignment="Center" 
      Margin="5,0,5,5" 
      Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"> 
    </TextBox> 
    <Popup PlacementTarget="{Binding ElementName=TextBox}"> 
     <ListView x:Name="ItemList" ItemsSource="{Binding Items}" SelectedItem ="{Binding SelectedItem}"> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <WrapPanel> 
         <Image Source="{Binding IconSource}" Width="20" Height="20"></Image> 
         <Label Content="{Binding SearchName}"></Label> 
        </WrapPanel> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
     </ListView> 
    </Popup> 
</Grid> 

视图模型

using System; 
using System.Collections.ObjectModel; 
using System.Linq; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows; 
using System.Windows.Input; 
using System.Windows.Interop; 
using System.Windows.Threading; 
using Caliburn.Micro; 
using QuickScope.Views; 

namespace QuickScope.ViewModels 
{ 
public class NavigatorViewModel : Screen 
{ 
    private readonly ItemService _itemService; 

    public NavigatorViewModel() 
    { 
     _itemService = new ItemService(); 
     Items = new ObservableCollection<ItemViewModel>(); 

    } 

    public ObservableCollection<ItemViewModel> Items { get; } 

    public ItemViewModel SelectedItem { get; set; } 

    private string _searchText; 

    public string SearchText 
    { 
     get => _searchText; 
     set 
     { 
      _searchText = value; 
      NotifyOfPropertyChange(() => SearchText); 
      UpdateItemList(_searchText); 
     } 
    } 

    private void UpdateItemList(string searchText) 
    { 
     Items.Clear(); 

     var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

     Task.Factory.StartNew(() => 
     { 
      //the long running workload 
      var items = _itemService.GetByFilter(searchText); 

      //update the ui 
      Task.Factory.StartNew(() => 
      { 
       foreach (var item in items) 
        Items.Add(item); 

       if (items.Any()) 
       { 
        SelectedItem = items.First(); 
        NotifyOfPropertyChange(() => SelectedItem); 
       } 
      }, CancellationToken.None, TaskCreationOptions.None, uiScheduler); 
     }); 
    } 
} 
} 

编辑

我尝试了Shadrix的方法,但不幸的是它也没有工作。我也尝试了Peter Dunihos'marked duplicate的答案,但是我没有成功(我得到了与上面描述的相同的XamlParseException)。

我试过的最后一件事是CM的构建方法OnUIThread,它基本上包装了Dispatcher.CurrentDispatcher。显然(考虑我最后两次失败的尝试),它也失败了。当前的实现看起来像下面这样(我删除了其他非相关的属性和方法):

public class NavigatorViewModel : Screen 
{ 
public NavigatorViewModel() 
{ 
    Items = new ObservableCollection<ItemViewModel>(); 
} 

public ObservableCollection<ItemViewModel> Items { get; set; } 

public ItemViewModel SelectedItem { get; set; } 

private string _searchText; 

public string SearchText 
{ 
    get => _searchText; 
    set 
    { 
     _searchText = value; 
     NotifyOfPropertyChange(() => SearchText); 
     UpdateItemList(_searchText); 
    } 
} 

private void UpdateItemList(string searchText) 
{ 
    Items.Clear(); 

    var updater = new ItemUpdater(); 
    updater.ItemsUpdated += (s, e) => { 
     OnUIThread(() => 
     { 
      var items = ((ItemsUpdatedEventArgs) e).Items; 
      foreach (var item in items) 
      { 
       Items.Add(item); 
      } 
      NotifyOfPropertyChange(() => Items); 
     }); 
    }; 
    var updateThread = new Thread(updater.GetItems); 
    updateThread.Start(searchText); 
} 
} 

public class ItemUpdater 
{ 
public event EventHandler ItemsUpdated; 
private readonly ItemService _itemService; 

public ItemUpdater() 
{ 
    _itemService = new ItemService(); 
} 

public void GetItems(object searchText) 
{ 
    var items = _itemService.GetByFilter((string)searchText); 

    ItemsUpdated?.Invoke(this, new ItemsUpdatedEventArgs(items)); 
} 
} 

public class ItemsUpdatedEventArgs : EventArgs 
{ 
public ItemsUpdatedEventArgs(IList<ItemViewModel> items) 
{ 
    Items = items; 
} 

public IList<ItemViewModel> Items { get; } 
} 

它的驾驶我坚果,我是不能够解决这个问题,所以如果有任何人在那里,会喜欢帮助一个年轻的青年,我会高度评价它。 :)

你可以找到完整的源代码here

谢谢大家!

回答

1

使用当前的UI线程的Dispatcher

//update the ui 
Application.Current.Dispatcher.BeginInvoke(new Action(() => 
{ 
    foreach (var item in items) 
    { 
     Items.Add(item); 
    } 

    if (items.Any()) 
    { 
     SelectedItem = items.First(); 
     NotifyOfPropertyChange(() => SelectedItem); 
    } 
})); 
+0

谢谢你的回答,遗憾的是它并没有解决我的问题。我仍然得到所述的例外(请参阅编辑的问题以获取更多信息) – CiniMod