开发者

Must create DependencySource on same Thread as the DependencyObject

I bind observable dictionary from view model to view. I use Caliburn Micro Framework.

View:

    <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()]" >

Code from view model class.

Properties look like this:

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

In Dispatcher timer I call every 3 seconds in seperate thread new service method.

So I constructor of view model I have this:

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

        _threadDispatcher = Dispatcher.CurrentDispatcher;

And Timer tick method is here:

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

when I run app I get this error:

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 a开发者_StackOverflow社区ffectedChildren, 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)

I try replace dispatcher:

this _threadDispatcher = Dispatcher.CurrentDispatcher;

with this: _threadDispatcher = Application.Current.Dispatcher;

But it doesn’t help. Thank for advice.

MyObservableDicionary is not dependency object or have dependecy property:

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


I encountered a similiar situation.

I bound an ObservableCollection of a class named Person to a datagrid, and Person.SkinColor is SolidColorBrush.

What I did was the following:

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


Just a guess, but Tasks get created on a background thread, by default. Try creating your task using the Task.Factory overload with a SynchronizationContext. I'm not sure if using the Dispatcher inside a Task works the way one would expect.

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

Once you do that, you should be able to modify your backing property without using the dispatcher.


For the sake of completeness I'd mention that the approved answer doesn't suit if you've got some objects that doesn't inherit Freezable class. The standard ObservableCollection only allows updates from the dispatcher thread, so you need a thread-safe analog. There are two solutions of WPF guru Dean Chalk what solve the problem:

  1. Create a thread-safe observable collection. It's an old school solution that just works. To get the source code check a short article in his blog.
  2. Use Reactive Extensions library. See this article for an example. It's a bit bulky for one task, but meanwhile it brings a bunch of modern tools which come in handy.

UPDATE (31 July 2015):

Links to Dean Chalk's blog are dead, so here are alternatives:

  • Thread-safe observable collection: article, source code.
  • Multi-threading, ObservableCollection, Reactive Extensions: article.


Is your data source a DependencyObject? If so, it needs to be created on the UI thread, too. Usually, you shouldn't need to inherit your datasource from DependencyObject though.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜