2017-09-27 1 views
-2

Wenn ich versuche, eine ObservableCollection, die ich in meinem XAML aus einem separaten Thread dann den ui Thread zu aktualisieren, bekomme ich eine XamlParseException, die besagt, dass die DependencySource auf dem gleichen Thread erstellt werden muss das DependencyObject. Ich verwende Caliurn Micro, um das ViewModel an die View zu binden.WPF Aktualisierung ObservableList von async Aufgabe löst XamlParseException

Ich habe versucht, ein paar Möglichkeiten, um mein Ziel zu erreichen, und die folgende scheint der vernünftigste Ansatz für mich. Ich übergebe den SyncronizationContext von der Benutzeroberfläche an die Aufgabe, damit er die ui nach der hohen Arbeitslast aktualisieren kann.

Was mache ich falsch?

Ansicht

<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> 

Ansichtsmodell

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); 
     }); 
    } 
} 
} 

EDIT

Ich habe Shadrix 'Ansatz versucht, aber leider auch nicht. Ich habe auch die Antworten von Peter Dunihos marked duplicate versucht, aber ich war auch nicht erfolgreich (ich bekomme die gleiche XamlParseException wie oben beschrieben).

Das letzte, was ich versuchte, ist CM in OnUIThread-Methode gebaut, die im Grunde den Dispatcher.CurrentDispatcher Wraps. Offensichtlich (in Anbetracht meiner letzten zwei gescheiterten Versuche) ist es auch gescheitert. Die aktuelle Implementierung sieht so etwas wie die folgenden: (i andere, nicht relevanten Eigenschaften und Methoden entfernt):

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; } 
} 

Es mich verrückt fährt, dass ich dieses Problem nicht in der Lage bin zu lösen, so dass, wenn es jemanden gibt, die würde Ich möchte gerne einem jungen Junior helfen, ich würde es sehr schätzen. :)

Sie können den vollständigen Quellcode here finden.

Danke euch allen!

Antwort

1

Verwenden Sie die aktuellen UI-Thread 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

danke Ihnen für Ihre Antwort, leider hat es nicht mein Problem lösen. Ich bekomme immer noch die beschriebene Ausnahme (siehe die bearbeitete Frage für weitere Informationen) – CiniMod