Подтвердить что ты не робот

Должен создать DependencySource в той же теме, что и DependencyObject

Я связываю наблюдаемый словарь из модели просмотра для просмотра. Я использую 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)

Я пытаюсь заменить диспетчера:

this _threadDispatcher = Dispatcher.CurrentDispatcher;

с этим: _threadDispatcher = Application.Current.Dispatcher;

Но это не помогает. Благодарим за совет.

MyObservableDicionary не является объектом зависимостей или имеет свойство dependecy:

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

Ответ 1

Я столкнулся с подобной ситуацией. Я привязал ObservableCollection класса с именем Person к datagrid, а Person.SkinColor - SolidColorBrush.

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

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

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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