c# wpf mvvm caliburn.micro dispatcher

c# - Debe crear DependencySource en el mismo subproceso que DependencyObject



wpf mvvm (4)

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

Enlazar diccionario observable de ver modelo a ver. Yo uso Caliburn Micro Framework.

Ver:

<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 vista modelo de clase.

Las propiedades se ven así:

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

En el temporizador de Dispatcher, llamo cada 3 segundos en un nuevo método de servicio por hilos separados.

Así que el constructor del modelo de vista tengo esto:

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

Y el método de tick 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 ejecuto la 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:

this _threadDispatcher = Dispatcher.CurrentDispatcher;

con esto: _threadDispatcher = Application.Current.Dispatcher;

Pero no ayuda. Gracias por el consejo.

MyObservableDicionary no es un objeto de dependencia o tiene propiedad de dependencia:

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


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

  1. Crea una colección observable segura para hilos. Es una solución de la vieja escuela que simplemente funciona. Para obtener el código fuente revisa un artículo corto en su blog .
  2. Usa la biblioteca de extensiones reactivas . Vea este artículo para un ejemplo. Es un poco voluminoso para una tarea, pero mientras tanto trae un montón de herramientas modernas que son útiles.

ACTUALIZACIÓN (31 de julio de 2015):

Los enlaces al blog de Dean Chalk están muertos, así que aquí hay alternativas:

  • Colección observable segura para subprocesos: article , código fuente .
  • Multi-threading, ObservableCollection, Extensiones reactivas: article .

Me encontré con una situación similar.

Vinculé un ObservableCollection de una clase llamada Person a un datagrid, 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();  ..... }


Solo una conjetura, pero las tareas se crean en un hilo de fondo, de forma predeterminada. Intente crear su tarea utilizando la sobrecarga de Task.Factory con un SynchronizationContext. No estoy seguro de si usar el 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 que haga eso, debería poder modificar su propiedad de respaldo sin usar el despachador.