web-dev-qa-db-ja.com

DependencyObjectと同じスレッドにDependencySourceを作成する必要があります

オブザーバブルディクショナリをビューモデルからビューにバインドします。 Caliburn MicroFrameworkを使用しています。

表示:

    <ListBox Name="Friends" 
             SelectedIndex="{Binding Path=SelectedFriendsIndex,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
             SelectedItem="{Binding Path=SelectedFriend, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
             Style="{DynamicResource friendsListStyle}"
             IsTextSearchEnabled="True" TextSearch.TextPath="Value.Nick"
             Grid.Row="2" 
             Margin="4,4,4,4"
             PreviewMouseRightButtonUp="ListBox_PreviewMouseRightButtonUp"
             PreviewMouseRightButtonDown="ListBox_PreviewMouseRightButtonDown" 
             MouseRightButtonDown="ListBox_MouseRightButtonDown"
             Micro:Message.Attach="[MouseDoubleClick]=[Action OpenChatScreen()]" >

ビューモデルクラスからのコード。

プロパティは次のようになります。

public MyObservableDictionary<string, UserInfo> Friends
{
    get { return _friends; }
    set
    {
        _friends = value;
        NotifyOfPropertyChange(() => Friends);
    }
}

ディスパッチャータイマーでは、3秒ごとに別のスレッドの新しいサービスメソッドを呼び出します。

だから私はビューモデルのコンストラクターを持っています:

        _dispatcherTimer = new DispatcherTimer();
        _dispatcherTimer.Tick += DispatcherTimer_Tick;
        _dispatcherTimer.Interval = TimeSpan.FromSeconds(3);
        _dispatcherTimer.Start();

        _threadDispatcher = Dispatcher.CurrentDispatcher;

そしてタイマーティックメソッドはここにあります:

    private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
    {
        new System.Threading.Tasks.Task(() =>
        {
            //get new data from server
            MyObservableDictionary<string, UserInfo> freshFriends = _service.GetFriends(Account);

            _threadDispatcher.BeginInvoke((System.Action)(() =>
            {
                //clear data, Friend is property which is binded on listobox control
                Friends.Clear();

                //here is problem - > refresh data
                foreach (var freshFriend in freshFriends)
                {
                    Friends.Add(freshFriend);

                }
            }));
        }).Start();

アプリを実行すると、次のエラーが発生します。

Must create DependencySource on same Thread as the DependencyObject.


   at System.Windows.Markup.XamlReader.RewrapException(Exception e, Uri baseUri)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlObjectWriter objectWriter)
   at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(DependencyObject container, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)
   at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren)
   at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)
   at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)
   at System.Windows.FrameworkElement.ApplyTemplate()
   at System.Windows.FrameworkElement.MeasureCore(Size availableSize)
   at System.Windows.UIElement.Measure(Size availableSize)
   at System.Windows.Controls.Border.MeasureOverride(Size constraint)

コーディネーターを交換してみます:

この _threadDispatcher = Dispatcher.CurrentDispatcher;

これとともに: _threadDispatcher = Application.Current.Dispatcher;

しかし、それは役に立ちません。アドバイスありがとうございます。

MyObservableDicionaryは依存関係オブジェクトではないか、依存関係プロパティを持っています:

public class MyObservableDictionary<TKey, TValue> :
    IDictionary<TKey, TValue>,
    INotifyCollectionChanged,
    INotifyPropertyChanged
{..}
17
user572844

私は同様の状況に遭遇しました。

Personという名前のクラスのObservableCollectionをデータグリッドにバインドしました。Person.SkinColorはSolidColorBrushです。

私がしたことは次のとおりです。

foreach (Person person in personData)
{
 PersonModel person= new Person( );
 ......               
 personModel.SkinColor = new SolidColorBrush(person.FavoriteColor);
 personModel.SkinColor.Freeze();
 .....
}
26
user2990634

推測ですが、デフォルトでは、タスクはバックグラウンドスレッドで作成されます。同期コンテキストでTask.Factoryオーバーロードを使用してタスクを作成してみてください。タスク内でディスパッチャを使用すると、期待どおりに機能するかどうかはわかりません。

var uiContext = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(() => { }, CancellationToken.None, TaskCreationOptions.None, uiContext);

これを行うと、ディスパッチャを使用せずにバッキングプロパティを変更できるようになります。

21
rrhartjr

完全を期すために、 Freezable クラスを継承しないオブジェクトがある場合、承認された回答は適切ではないことを述べておきます。標準のObservableCollectionは、ディスパッチャスレッドからの更新のみを許可するため、スレッドセーフなアナログが必要です。 WPFの第一人者には2つの解決策があります Dean Chalk 問題を解決するもの:

  1. スレッドセーフな監視可能なコレクションを作成します。これは、機能する古い学校のソリューションです。ソースコードを入手するには、 彼のブログの短い記事 を確認してください。
  2. Reactive Extensions ライブラリを使用します。例については、 この記事 を参照してください。 1つのタスクには少しかさばりますが、その一方で、便利な最新のツールがたくさんあります。

更新(2015年7月31日):

Dean Chalkのブログへのリンクは無効になっているため、代替手段は次のとおりです。

  • スレッドセーフな監視可能なコレクション: 記事ソースコード
  • マルチスレッド、ObservableCollection、Reactive Extensions: 記事
3
Alex Klaus

データソースはDependencyObjectですか?その場合は、UIスレッドでも作成する必要があります。ただし、通常は、DependencyObjectからデータソースを継承する必要はありません。

2
Botz3000