Необходимо создать DependencySource в том же потоке, что и DependencyObject. - PullRequest
17 голосов
/ 14 января 2011

Я привязываю наблюдаемый словарь от модели представления к представлению. Я использую Caliburn Micro Framework.

Вид:

    <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
{..}

Ответы [ 4 ]

23 голосов
/ 14 ноября 2013

Я столкнулся с похожей ситуацией.

Я связал ObservableCollection класса с именем Person с сеткой данных, и Person.SkinColor - это SolidColorBrush.

Я сделал следующее:

foreach (Person person in personData)
{
 PersonModel person= new Person( );
 ......               
 personModel.SkinColor = new SolidColorBrush(person.FavoriteColor);
 personModel.SkinColor.Freeze();
 .....
}
20 голосов
/ 15 января 2011

Просто предположение, но по умолчанию задачи создаются в фоновом потоке. Попробуйте создать задачу, используя перегрузку Task.Factory с SynchronizationContext. Я не уверен, что использование Dispatcher внутри Задачи работает так, как можно было бы ожидать.

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

Как только вы это сделаете, вы сможете изменить свой резервный ресурс без использования диспетчера.

3 голосов
/ 23 августа 2013

Для полноты картины упомяну, что одобренный ответ не подходит, если у вас есть объекты, которые не наследуют класс Freezable . Стандартная коллекция ObservableCollection разрешает обновления только из потока диспетчера, поэтому вам необходим поточно-безопасный аналог. Есть два решения WPF гуру Дин Мел , которые решают проблему:

  1. Создать потокобезопасную наблюдаемую коллекцию. Это решение старой школы, которое просто работает. Чтобы получить исходный код, просмотрите короткую статью в своем блоге .
  2. Использование библиотеки Reactive Extensions . См. эту статью для примера. Это немного громоздко для одной задачи, но в то же время она приносит кучу современных инструментов, которые пригодятся.

ОБНОВЛЕНИЕ (31 июля 2015 г.):

Ссылки на блог Дина Чока мертвы, поэтому есть альтернативы:

2 голосов
/ 14 января 2011

Является ли ваш источник данных объектом DependencyObject?Если это так, его также необходимо создать в потоке пользовательского интерфейса.Обычно вам не нужно наследовать ваш источник данных от DependencyObject.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...