2011-06-13 8 views
2

Je suis relativement nouveau sur MVVM et j'essaye de coder une barre d'état de base pour une application MVVM WPF. Je pense avoir le sens des choses, mais pour une raison quelconque, la barre d'état ne se met pas toujours à jour, et je ne sais pas pourquoi.StatusBar ne met pas toujours à jour

Dans mon ViewModel, j'ai une propriété de base que je mets à jour lorsque je change un message d'état:

public string StatusMessage 
{ 
    get { return _statusMessage; } 
    set 
    { 
     if (value == _statusMessage) return; 
     _statusMessage = value; 
     base.OnPropertyChanged(() => this.StatusMessage); 
    } 
} 

Ma méthode OnPropertyChanged (que j'ai dans une classe ViewModel de base qui implémente INotifyPropertyChanged) ressemble à ceci (eu cette idée de Gunther Foidl, souhaite que je pourrais demander un crédit pour cela parce que je pense qu'il est lisse mais je ne suis pas tout à fait aussi intelligent):

protected virtual void OnPropertyChanged<T>(Expression<Func<T>> exp) 
    { 
    MemberExpression me = exp.Body as MemberExpression; 
    string propName = me.Member.Name; 

    PropertyChangedEventHandler handler = this.PropertyChanged; 
    if (handler != null) 
    { 
     handler(this, new PropertyChangedEventArgs(propName)); 
    } 
} 

en tout cas, tout cela fonctionne très bien pour tous mes contrôles sauf un. Sur mon fichier MainWindow.xaml, j'ai un contrôle StatusBarItem lié à la propriété ci-dessus, comme si (le reste du XAML a été coupé pour des raisons d'espace):

<StatusBarItem Grid.Column="0"> 
    <TextBlock TextTrimming="CharacterEllipsis" Text="{Binding Path=StatusMessage}" /> 
</StatusBarItem> 

Quand je lance mon application (qui frappe un deux DB en plus de générer un document à partir d'un modèle et un tas d'autres choses assez gourmandes en ressources), certains messages, mais pas tous, s'affichent dans la barre d'état. J'ai débogué et vérifié que les messages le font tous dans la propriété StatusMessage, ci-dessus (et la variable privée qui en découle), ils ne semblent tout simplement pas rafraîchir dans l'interface utilisateur.

J'ai regardé plusieurs exemples qui utilisent des instances BackgroundWorker pour les contrôles ProgressBar, mais n'en ont vu aucun pour les contrôles StatusBarItem, et je ne suis pas vraiment sûr de savoir comment traduire les uns vers les autres. J'ai également utilisé des tâches auparavant dans les applications précédentes C# 4.0 et WPF, et je me dis que c'est probablement un bon moyen d'y aller, mais je n'ai pas vraiment été capable de comprendre comment/où désigner la tâche de l'interface utilisateur (I ' Je l'ai toujours fait dans le code-behind pour MainWindow avant, mais je cherche un code-zéro pour rester en accord avec MVVM ici). Je suis à peu près sûr qu'une approche multi-thread est la voie à suivre; Je n'en sais pas assez sur une approche (j'en connais un peu et un peu de ça) pour que ça marche. J'ai vu quelques messages qui utilisaient l'ancienne approche de threading directement, mais je suis resté à l'écart de la programmation multithread jusqu'à ce que je commence à utiliser Tasks avec .NET 4.0 (les trouvant un peu plus faciles à comprendre et à suivre), alors j'avais un peu de difficulté à les comprendre.

Quelqu'un peut-il avoir pitié de moi et me diriger dans la bonne direction, ou suggérer un débogage supplémentaire que je peux faire? Merci!

+0

J'ai une mise à jour similaire à un problème TextBlock, avez-vous trouvé une solution à la fin? –

+0

Je pouvais ** faire fonctionner le code de la tâche, en passant mon TaskScheduler de l'interface utilisateur dans mon ViewModel et en effectuant la mise à jour sur une tâche engendrée par ledit planificateur. Je posterai des exemples de code dans une réponse séparée, si le temps le permet. –

+0

@Julien, voici ce que j'ai fait. Lors de l'initialisation de mon ViewModel dans mon fichier App.xaml.cs, j'ai transmis 2 instances de TaskFactory: Une pour l'interface utilisateur: 'new TaskFactory (TaskScheduler.FromCurrentSynchronizationContext())' que j'ai appelé 'UiTasks', et un pour tout le reste' Tâche .Factory' que j'ai appelé 'DefaultTasks'. Ensuite, je me suis assuré que mon gros travail s'exécutait sous la fabrique de tâches par défaut, et mis à jour ma propriété en utilisant l'interface utilisateur: 'this.UiTasks.StartNew (() => this.StatusMessage = myMessage;});' Cela semblait faire l'affaire . Je serai heureux de vous envoyer un exemple plus détaillé si vous le souhaitez. Bonne chance! –

Répondre

3

1) La liaison basée sur la réflexion peut être source d'erreur, parfois en raison de l'inlining. Essayez de voir ce qui se passe si vous notifiezpropertychanged avec une chaîne simple au lieu de la réflexion.2) si vous utilisez plusieurs threads, il se peut que vous ne configuriez pas StatusMessage à partir de UIThread. Dans ce cas, vous ne pourrez pas mettre à jour l'interface utilisateur. Vous pouvez invoquer le code Setter sur UI Dispatcher pour voir si cela peut aider

3) vérifier si les travaux de liaison, dans le constructeur de la forme de XAML modifier StatusMessage directement sur VM et voir si le changement est affiché sur l'interface utilisateur sans invoquer les appels de service multithread qui introduisent des variables supplémentaires simples textblock - chaîne de liaison

4) Si cela ne vous aide pas, vous pouvez créer une simple forme xaml avec un seul bloc de texte lier à votre grand viewmodel et voir ce qui se passe, si rien ne fonctionne, vous pouvez commencer n coupe classe VM pour le rendre plus simple si contraignant commence finalement à travailler et vous trouvez une erreur

5) si vous pensez que la barre d'état est le problème voir si textblock unique sans barre d'état (extrait de partie XAML de votre exemple) fonctionne

+0

Merci, Valentin. Comme il s'est avéré, le problème ** était ** que le message d'état n'était pas mis à jour à partir du thread UI (et à ce moment je ne pouvais pas comprendre comment faire avec MVVM). Une fois que j'ai été en mesure de configurer l'interface utilisateur TaskScheduler/TaskFactory dans ma routine App_Startup et le passer dans le constructeur de mon ViewModel, puis l'utiliser pour générer une tâche pour mettre à jour le statut, cela a fonctionné comme un charme. Merci! –

+0

Je suis confus pourquoi le fil d'origine est un problème. WPF est destiné à rassembler les notifications sur les liaisons sur le thread de l'interface utilisateur automatiquement n'est-ce pas? – Gusdor

2

Quelque part la notification ne passe pas.

Je voudrais essayer:

  • Ajouter un valueconverter factice sur le textbinding de sorte que vous pouvez définir un point d'arrêt et voir si vous êtes appelés
  • Dispatching la propriété mis à définir la valeur à la fois « mieux » - C'est parfois nessaire.

La distribution de l'ensemble pourrait faire l'affaire.