Must create DependencySource on same Thread as the DependencyObject

32,008

Solution 1

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

Solution 2

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.

Solution 3

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.

Solution 4

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.

Share:
32,008
Admin
Author by

Admin

Updated on July 09, 2022

Comments

  • Admin
    Admin almost 2 years

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

    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
    {..}
    
  • Admin
    Admin over 13 years
    What do you mean with data source ? Property Friend?
  • Botz3000
    Botz3000 over 13 years
    Your view model, or anything that is bound to the UI.
  • Admin
    Admin over 13 years
    No, VM is create with Caliburn Micro, in this class I haven’t any dependecy properties and objects.
  • Botz3000
    Botz3000 over 13 years
    Maybe your ObservableDictionary? Try creating that on the UI thread.
  • Admin
    Admin over 13 years
    I try init variable freshFriends out of scope dispatcherTimer_tick event, in constructor of VM. But problem is same. MyObservable dic is ok in my view, no dependency property or object in this class.
  • RobV
    RobV almost 9 years
    Great answer - yes this approach does work, solved a problem I'd been hitting my head against for far too long.
  • JumpingJezza
    JumpingJezza almost 9 years
    your link is ded ;( Perhaps you could copy the information here instead of linking elsewhere?
  • Alex Klaus
    Alex Klaus almost 9 years
    @JumpingJezza, I added alternative links. Both solutions are too bulky for a post.
  • JumpingJezza
    JumpingJezza almost 9 years
    Not to worry. I had an Observable Collection of type ImageSource and simply freezing the ImageSource worked.
  • smerlung
    smerlung over 7 years
    I had the same problem with an ImageSource. Calling Freeze() also worked in my case
  • FindOutIslamNow
    FindOutIslamNow about 7 years
    But this does not execute the task in background anymore, right? it becomes an on-ui-operation.