2011-01-14 41 views
17

Adjunto el diccionario observable del modelo de vista para ver. Yo uso Caliburn Micro Framework.Debe crear DependencySource en el mismo subproceso que el objeto DependencyObject

Vista:

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

Código de la clase de vista del modelo.

Propiedades aspecto:

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

En temporizador despachador me llaman cada 3 segundos en el método nuevo servicio hilo separado.

constructor Así que de vista del modelo tengo esto:

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

     _threadDispatcher = Dispatcher.CurrentDispatcher; 

y el método de paso del temporizador está aquí:

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

cuando corro aplicación me sale este 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) 

Intento reemplazar despachador:

este _threadDispatcher = Dispatcher.CurrentDispatcher;

con esto: _threadDispatcher = Application.Current.Dispatcher;

Pero no ayuda. Gracias por el consejo.

MyObservableDicionary no es dependencia de los objetos o tienen la propiedad dependecy:

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

Respuesta

2

Es el origen de datos DependencyObject? Si es así, también debe crearse en el hilo de la interfaz de usuario. Por lo general, no debería necesitar heredar su fuente de datos de DependencyObject.

+0

¿Qué quiere decir con la fuente de datos? ¿Amigo de la propiedad? –

+0

Su modelo de vista, o cualquier cosa que esté vinculada a la interfaz de usuario. – Botz3000

+0

No, VM es crear con Caliburn Micro, en esta clase no tengo propiedades ni objetos de dependencia. –

20

Solo supongo, pero las Tareas se crean en un hilo de fondo, de forma predeterminada. Intente crear su tarea usando la sobrecarga Task.Factory con un SynchronizationContext. No estoy seguro de si el uso del Dispatcher dentro de una Tarea funciona de la manera que uno esperaría.

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

Una vez hecho esto, debe poder modificar su propiedad de respaldo sin utilizar el despachador.

+1

Excelente respuesta - Sí, este enfoque funciona, resolvió un problema al que me había estado golpeando durante demasiado tiempo. – RobV

+0

Pero esto ya no ejecuta la tarea en segundo plano, ¿verdad? se convierte en una operación en-ui. – FindOutIslamNow

2

En aras de la exhaustividad, mencionaría que la respuesta aprobada no es adecuada si tiene algunos objetos que no heredan la clase Freezable. El ObservableCollection estándar solo permite actualizaciones del hilo del despachador, por lo que necesita un análogo seguro para subprocesos. Hay dos soluciones de gurú WPF Dean Chalk lo resuelven el problema:

  1. crear una colección observable flujos seguros. Es una solución de la vieja escuela que simplemente funciona. Para obtener el código fuente, verifique short article in his blog.
  2. Usar la biblioteca Reactive Extensions. Ver this article para un ejemplo. Es un poco abultado para una tarea, pero mientras tanto trae un montón de herramientas modernas que son útiles.

ACTUALIZACIÓN (31 de julio de 2015):

Enlaces a el blog de Dean tiza están muertos, así que aquí están las alternativas:

  • Hilo de seguridad colección observables: article, source code.
  • Multi-threading, ObservableCollection, Extensiones reactivas: article.
+0

su enlace está dedicado; (¿Quizás podría copiar la información aquí en lugar de enlazar en otro lugar? – JumpingJezza

+0

@JumpingJezza, agregué enlaces alternativos. Ambas soluciones son demasiado voluminosas para una publicación. –

+1

No se preocupe. Tenía una Colección Observable del tipo ImageSource y simplemente congelando ImageSource funcionó. – JumpingJezza

23

Encontré una situación similar.

Adjunté una ObservableCollection de una clase llamada Person a una cuadrícula de datos, y Person.SkinColor es SolidColorBrush.

Lo que hice fue lo siguiente:

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

Tuve el mismo problema con ImageSource. Calling Freeze() también funcionó en mi caso – smerlung

Cuestiones relacionadas