Must create DependencySource on same Thread as the DependencyObject
Asked Answered
A

4

21

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
{..}
Assagai answered 14/1, 2011 at 11:56 Comment(0)
J
34

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();
 .....
}
Jacintajacinth answered 14/11, 2013 at 5:28 Comment(1)
I had the same problem with an ImageSource. Calling Freeze() also worked in my caseKessia
D
24

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.

Deepseated answered 14/1, 2011 at 21:26 Comment(2)
Great answer - yes this approach does work, solved a problem I'd been hitting my head against for far too long.Est
But this does not execute the task in background anymore, right? it becomes an on-ui-operation.Wayzgoose
U
4

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.
Upbraiding answered 23/8, 2013 at 0:9 Comment(3)
your link is ded ;( Perhaps you could copy the information here instead of linking elsewhere?Arequipa
@JumpingJezza, I added alternative links. Both solutions are too bulky for a post.Upbraiding
Not to worry. I had an Observable Collection of type ImageSource and simply freezing the ImageSource worked.Arequipa
M
3

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.

Mouldon answered 14/1, 2011 at 12:25 Comment(5)
What do you mean with data source ? Property Friend?Assagai
Your view model, or anything that is bound to the UI.Mouldon
No, VM is create with Caliburn Micro, in this class I haven’t any dependecy properties and objects.Assagai
Maybe your ObservableDictionary? Try creating that on the UI thread.Mouldon
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.Assagai

© 2022 - 2024 — McMap. All rights reserved.