Existe-t-il une méthode simple permettant d’inviter l’utilisateur à confirmer un changement de sélection dans la liste déroulante et à ne pas le traiter si l’utilisateur a sélectionné non?
Nous avons une liste déroulante dans laquelle le fait de modifier la sélection entraînera une perte de données. Fondamentalement, l'utilisateur sélectionne un type, puis il est capable d'entrer des attributs de ce type. S'ils changent de type, tous les attributs sont effacés, car ils risquent de ne plus être applicables. Le problème est que, sous la sélection, vous déclenchez à nouveau l'événement SelectionChanged
.
Voici un extrait:
if (e.RemovedItems.Count > 0)
{
result = MessageBox.Show("Do you wish to continue?",
"Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);
if (result == MessageBoxResult.No)
{
if (e.RemovedItems.Count > 0)
((ComboBox)sender).SelectedItem = e.RemovedItems[0];
else
((ComboBox)sender).SelectedItem = null;
}
}
J'ai deux solutions qui ne me plaisent pas.
Une fois que l'utilisateur a sélectionné 'Non' , supprimez le gestionnaire d'événements SelectionChanged
, modifiez l'élément sélectionné, puis enregistrez à nouveau le gestionnaire d'événements SelectionChanged
. Cela signifie que vous devez conserver une référence du gestionnaire d'événements dans la classe afin de pouvoir l'ajouter et le supprimer.
Créez un booléen ProcessSelectionChanged
dans le cadre de la classe. Vérifiez-le toujours au début du gestionnaire d'événements. Définissez-le sur false avant de revenir à la sélection, puis sur True. Cela fonctionnera, mais je n'aime pas utiliser les indicateurs pour annuler un gestionnaire d'événements.
Quelqu'un at-il une solution alternative ou une amélioration sur celles que je mentionne?
Je me souviens d'avoir eu besoin de faire cela il y a un moment. Il m'a fallu environ une semaine de recherches et d'essais avant de trouver une bonne solution. Je l'ai posté ici:
WPF: Annuler une sélection d'utilisateur dans un ListBox lié aux données?
Pour votre information, c'est une solution basée sur M-V-VM (si vous n'utilisez pas le modèle M-V-VM, vous devriez le faire!)
J'ai trouvé cette bonne implémentation.
private bool handleSelection=true;
private void ComboBox_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
if (handleSelection)
{
MessageBoxResult result = MessageBox.Show
("Continue change?", MessageBoxButton.YesNo);
if (result == MessageBoxResult.No)
{
ComboBox combo = (ComboBox)sender;
handleSelection = false;
combo.SelectedItem = e.RemovedItems[0];
return;
}
}
handleSelection = true;
}
source: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html
Peut-être créer une classe dérivée de ComboBox
et remplacer la OnSelectedItemChanged
(ou OnSelectionChangeCommitted
.)
La validation dans le gestionnaire d'événements SelectionChanged
vous permet d'annuler votre logique si la sélection est invalide, mais je ne connais pas de moyen facile d'annuler l'événement ou la sélection d'élément.
Ma solution consistait à sous-classer la liste déroulante WPF et à ajouter un gestionnaire interne pour l'événement SelectionChanged
. Chaque fois que l'événement est déclenché, mon gestionnaire interne privé déclenche à la place un événement SelectionChanging
personnalisé.
Si la propriété Cancel
est définie sur la SelectionChangingEventArgs
correspondante, l'événement n'est pas déclenché et la SelectedIndex
est rétablie à sa valeur précédente. Sinon, une nouvelle SelectionChanged
est générée et masque l’événement de base. Espérons que cela aide!
EventArgs et délégué de gestionnaire pour l'événement SelectionChanging:
public class SelectionChangingEventArgs : RoutedEventArgs
{
public bool Cancel { get; set; }
}
public delegate void
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);
Implémentation de classe ChangingComboBox:
public class ChangingComboBox : ComboBox
{
private int _index;
private int _lastIndex;
private bool _suppress;
public event SelectionChangingEventHandler SelectionChanging;
public new event SelectionChangedEventHandler SelectionChanged;
public ChangingComboBox()
{
_index = -1;
_lastIndex = 0;
_suppress = false;
base.SelectionChanged += InternalSelectionChanged;
}
private void InternalSelectionChanged(Object s, SelectionChangedEventArgs e)
{
var args = new SelectionChangingEventArgs();
OnSelectionChanging(args);
if(args.Cancel)
{
return;
}
OnSelectionChanged(e);
}
public new void OnSelectionChanged(SelectionChangedEventArgs e)
{
if (_suppress) return;
// The selection has changed, so _index must be updated
_index = SelectedIndex;
if (SelectionChanged != null)
{
SelectionChanged(this, e);
}
}
public void OnSelectionChanging(SelectionChangingEventArgs e)
{
if (_suppress) return;
// Recall the last SelectedIndex before raising SelectionChanging
_lastIndex = (_index >= 0) ? _index : SelectedIndex;
if(SelectionChanging == null) return;
// Invoke user event handler and revert to last
// selected index if user cancels the change
SelectionChanging(this, e);
if (e.Cancel)
{
_suppress = true;
SelectedIndex = _lastIndex;
_suppress = false;
}
}
}
Dans WPF, définissez dynamiquement l'objet avec
if (sender.IsMouseCaptured)
{
//perform operation
}
Je ne crois pas que l’utilisation du répartiteur pour publier (ou retarder) une mise à jour de propriété soit une bonne solution, c’est plus une solution de contournement qui n’est pas vraiment nécessaire. La solution suivante est entièrement mvvm et ne nécessite pas de répartiteur.
Dans le code de visualisation situé derrière l'événement, SelectionChanged met à jour la source (c'est-à-dire la machine virtuelle) ou la cible (c'est-à-dire le V), selon que la méthode VM.ConfirmChange (...) renvoie la valeur comme suit:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(e.AddedItems.Count != 0)
{
var selectedItem = e.AddedItems[0];
if (e.AddedItems[0] != _ViewModel.SelectedFormatType)
{
var comboBoxSelectedItemBinder = _TypesComboBox.GetBindingExpression(Selector.SelectedItemProperty); //_TypesComboBox is the name of the ComboBox control
if (_ViewModel.ConfirmChange(selectedItem))
{
// Update the VM.SelectedItem property if the user confirms the change.
comboBoxSelectedItemBinder.UpdateSource();
}
else
{
//otherwise update the view in accordance to the VM.SelectedItem property
comboBoxSelectedItemBinder.UpdateTarget();
}
}
}
}