Annuler zone de liste déroulante de sélection en WPF avec MVVM
J'ai une zone de liste déroulante dans mon application WPF:
<ComboBox ItemsSource="{Binding CompetitorBrands}" DisplayMemberPath="Value"
SelectedValuePath="Key" SelectedValue="{Binding Path=CompMfgBrandID, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Text="{Binding CompMFGText}"/>
Lié à une collection de KeyValuePair<string, string>
Ici est la CompMfgBrandID bien dans mon ViewModel:
public string CompMfgBrandID
{
get { return _compMFG; }
set
{
if (StockToExchange != null && StockToExchange.Where(x => !string.IsNullOrEmpty(x.EnteredPartNumber)).Count() > 0)
{
var dr = MessageBox.Show("Changing the competitor manufacturer will remove all entered parts from the transaction. Proceed?",
"Transaction Type", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (dr != DialogResult.Yes)
return;
}
_compMFG = value;
StockToExchange.Clear();
...a bunch of other functions that don't get called when you click 'No'...
OnPropertyChanged("CompMfgBrandID");
}
}
Si vous choisissez "oui", il se comporte comme prévu. Les effets sont compensés et les autres fonctions sont appelées. Si je choisis "Non", elle se retourne et ne pas effacer de ma liste ou d'appeler des autres fonctions, ce qui est bon, mais la zone de liste déroulante affiche toujours de la nouvelle sélection. J'en ai besoin pour revenir à la sélection d'origine, comme si rien n'avait changé, lorsque l'utilisateur sélectionne "Non". Comment puis-je accomplir? J'ai aussi essayé d'ajouter e.Handled = true
dans le code-behind, en vain.
- Mettre GUI à l'intérieur d'une propriété setter est une mauvaise idée. Surtout dans MVVM.
- Je recommande considérant ayant la propriété setter ou une propriété d'événements de changement de causer un message publié, et l'effet de levier le médiateur modèle pour gérer l'affichage de la boîte de dialogue de l'INTERFACE utilisateur. Boîte de dialogue de sélection serait alors publiez un message de réponse de votre modèle de vue du médiateur est à l'écoute pour.
- double possible de Comment la restauration sélectionné SelectedValue de la zone de liste modifiable à l'aide de WPF MVVM
- Notez que le problème peut être fait pour aller si vous pouvez modifier le code pour utiliser
SelectedItem
plutôt queSelectedValue
. Clairement, ce n'est pas un droit de swap, si vous avez besoin d'appliquer une certaine pensée.
Vous devez vous connecter pour publier un commentaire.
Pour atteindre ce sous MVVM....
1] Ont un comportement attaché à un qui gère la
SelectionChanged
cas de la zone de liste déroulante. Cet événement est déclenché avec certains des événements les arguments qui ontHandled
drapeau. Mais la valeur true est inutile pourSelectedValue
de liaison. La liaison des mises à jour de la source, indépendamment de savoir si l'événement a été géré.2] Donc nous allons configurer le
ComboBox.SelectedValue
liaison àTwoWay
etExplicit
.3] Uniquement lorsque votre vérifier est satisfait et messagebox dit
Yes
est lorsque nous effectuonsBindingExpression.UpdateSource()
. Sinon, nous appelons simplement laBindingExpression.UpdateTarget()
pour revenir à l'ancien de sélection.Dans mon exemple ci-dessous, j'ai une liste de
KeyValuePair<int, int>
lié au contexte de données de la fenêtre. LeComboBox.SelectedValue
est lié à une simple écritureMyKey
propriété de laWindow
.XAML ...
Où
MyDGSampleWindow
est le x:Nom de laWindow
.Code Derrière ...
Et la comportement attaché à
Dans le comportement je utiliser
ComboBox.Tag
propriété de stocker temporairement un drapeau qui saute le revérifier quand on revenir à l'ancienne valeur sélectionnée.Laissez-moi savoir si cela aide.
Ceci peut être réalisé dans un générique et de façon compacte à l'aide du Mélange Comportement Générique.
Le comportement définit une propriété de dépendance nommé
SelectedItem
, et vous devriez mettre votre liaison dans cette propriété, plutôt que dans la zone de liste déroulante duSelectedItem
de la propriété. Le comportement est en charge de transmettre les changements dans la propriété de dépendance à la zone de liste déroulante (ou, plus généralement, pour le Sélecteur), et lorsque le Sélecteur deSelectedItem
changements, il tente d'attribuer à ses propresSelectedItem
de la propriété. Si la mission échoue (probablement parce que la limite de la VM si elle setter rejeté la mission), le comportement des mises à jour du Sélecteur de l'SelectedItem
avec la valeur actuelle de sesSelectedItem
propriété.Pour toutes sortes de raisons, vous risquez de rencontrer des cas où la liste des éléments dans le Sélecteur est effacé, et l'élément sélectionné devient nulle (voir cette question). D'habitude vous ne voulez pas que votre VM propriété à devenir nulle dans ce cas. Pour cela, j'ai ajouté le IgnoreNullSelection propriété de dépendance, ce qui est vrai par défaut. Cela devrait résoudre un tel problème.
C'est le
CancellableSelectionBehavior
classe:C'est la façon de l'utiliser dans le code XAML:
et c'est un échantillon de la machine virtuelle de la propriété:
Solution très simple pour .NET 4.5.1+:
Il travaille pour moi dans la plupart des cas.
Vous pouvez reprendre la sélection dans la zone de liste déroulante, il suffit de le lancer NotifyPropertyChanged sans affectation de la valeur.
Je l'ai trouvé beaucoup plus simple de répondre à cette question par l'utilisateur shaun sur un autre thread:
https://stackoverflow.com/a/6445871/2340705
Le problème de base est que la propriété a changé événement est avalé. Certains auraient appelé ce un bug. Pour obtenir autour de cette utilisation BeginInvoke de le Répartiteur de la force de la propriété a changé de l'événement pour mettre à l'arrière sur la fin de l'événement l'INTERFACE utilisateur de la file d'attente. Cela oblige pas à modifier le code xaml, pas de supplément de classes de comportement, et une seule ligne de code modifié pour le modèle de vue.
Le problème est qu'une fois WPF mises à jour de la valeur du bien setter, il ignore toute autre propriété a changé de notifications à partir de l'intérieur de cet appel: il suppose qu'elles se produisent comme une partie normale de l'incubateur et sont sans conséquence, même si vous avez vraiment mis à jour la propriété à la valeur d'origine.
La façon dont je suis arrivé c'était pour permettre le champ à mettre à jour, mais aussi de la file d'attente d'une action sur le Répartiteur de "défaire" le changement. L'action se définir à l'ancienne valeur et l'incendie d'une notification de modification de propriété pour obtenir WPF pour réaliser que ce n'est pas vraiment la nouvelle valeur de ce qu'il en est.
Évidemment le "undo" action devrait être mis en place afin de ne pas le feu à toute la logique métier dans votre programme.
J'ai eu le même problème, causes par le thread de l'INTERFACE utilisateur et la façon dont biding œuvres. De vérifier la ce lien: SelectedItem sur la zone de liste déroulante
La structure dans l'exemple de code utilise derrière mais le MVVM est exactement le même.
System.Windows.Interactivity.Behavior
.Je préfère "splintor du" code de l'échantillon sur "AngelWPF de l'". Leurs approches sont assez similaires, quoique. J'ai mis en œuvre le comportement attaché, CancellableSelectionBehavior, et il fonctionne comme annoncé. Peut-être qu'il était juste que le code de splintor l'exemple était plus facile à brancher dans mon application. Le code de la AngelWPF du comportement attaché a des références à un KeyValuePair Type qui aurait appelé pour plus de code altération.
Dans mon application, j'ai une zone de liste déroulante où les éléments sont affichés dans une grille de données sont basées sur l'élément sélectionné dans la liste déroulante. Si l'utilisateur a apporté des modifications à la grille de données, puis sélectionné un nouvel élément dans la zone de liste déroulante, je voudrais inviter l'utilisateur à enregistrer les modifications avec Oui|NON|Annuler les boutons d'options. Si ils ont appuyé sur Annuler, je voulais ignorer leur nouvelle sélection dans la liste déroulante et garder l'ancienne sélection. Cela a fonctionné comme un champion!
Pour ceux qui effraient le moment ils de voir des références à Mélange et du Système.De Windows.L'interactivité, vous n'avez pas Microsoft Expression Blend installé. Vous pouvez télécharger le Blend SDK pour .NET 4 (ou Silverlight).
Blend SDK pour .NET 4
Blend SDK pour Silverlight 4
Oh ouais, dans mon XAML, j'utilise ce que ma déclaration d'espace de noms pour se Fondre dans cet exemple:
Ici est le flux général que j'utilise (n'a pas besoin de tout comportement ou XAML modifications):
(Si votre logique métier exige de l'élément sélectionné à ne pas être dans un état non valide, je suggère le déplacement que pour le côté du Modèle). Cette approche est aussi convivial pour les listes qui sont rendus à l'aide de Boutons Radio de la SelectedItem le setter de la sortie le plus rapidement possible n'empêchera pas les boutons radio d'être mis en évidence lorsqu'une boîte de message s'affiche.
J'ai mis tout annuler la logique gestionnaire et l'appeler à l'aide SynchronizationContext.Post()
(BTW: SynchronizationContext.Post fonctionne également pour les Applications du Windows Store. Donc, si vous avez partagé ViewModel code, cette approche fonctionne encore).
Je n'ai, d'une manière similaire à ce qui splintor a ci-dessus.
Votre point de vue:
Ci-dessous est le code du gestionnaire d'événement "ComboBox_SelectionChanged" à partir du fichier de code behind de la vue. Par exemple, si vous affichez est mavue.xaml, le code de nom de fichier pour ce gestionnaire d'événement doit être mavue.xaml.cs
Je pense que le problème est que la zone de liste déroulante définit l'élément sélectionné en tant que résultat de l'action de l'utilisateur après le réglage de la limite de la valeur de propriété. Ainsi, la zone de liste déroulante élément change pas d'importance ce que vous faites dans le ViewModel. J'ai trouvé une approche différente où vous n'avez pas à plier le pattern MVVM. Voici mon exemple (désolé qu'il est copié de mon projet et ne correspond pas exactement les exemples ci-dessus):
La différence est que je suis entièrement d'effacer les éléments de la collection et le remplir avec les éléments stockés avant. Cela oblige la zone de liste déroulante mise à jour tant que je suis en utilisant le ObservableCollection classe générique. Puis-je régler l'élément sélectionné à l'arrière de l'élément sélectionné qui a été défini précédemment. Ce n'est pas recommandé pour un grand nombre d'éléments en raison de compensation et de remplissage de la zone de liste déroulante est un peu cher.
Je voudrais pour terminer splintor réponse parce que je suis tombé sur un problème avec le retard de l'initialisation dans
OnSelectedItemChanged
:Quand OnSelectedItemChanged est soulevée devant AssociatedObject est affecté, à l'aide de la
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke
peut avoir des effets secondaires indésirables, tels que de l'initialisation de l'newValue avec la valeur par défaut de la zone de liste déroulante de sélection.Donc, même si votre ViewModel est jusqu'à ce jour, le comportement sera le déclencheur d'un changement à partir de ce Dernier est
SelectedItem
valeur actuelle de la sélection par défaut de la zone de liste déroulante stockées danse.NewValue
. Si votre code déclenche une Boîte de Dialogue, l'utilisateur sera averti d'un changement bien qu'il n'y est aucune. Je ne peux pas expliquer pourquoi cela arrive, probablement un problème de synchronisation.Voici mon fix
--Xaml
--ViewModel