web-dev-qa-db-fra.com

Comment annuler un événement ComboBox SelectionChanged?

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.

  1. 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.

  2. 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?

18
WPFNewbie

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!)

16
Aphex

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

18
Jaider

Peut-être créer une classe dérivée de ComboBox et remplacer la OnSelectedItemChanged (ou OnSelectionChangeCommitted.)

1
ispiro

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;
        }
    }
}
1
Rob

Dans WPF, définissez dynamiquement l'objet avec 

    if (sender.IsMouseCaptured)
    {
      //perform operation
    }
0
Subhash Saini

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. 

  • Commencez par lier l'élément SelectedItem à un mode de liaison explicite. // cela nous permet de décider si Valider à l'aide de la méthode UpdateSource () les modifications apportées à la VM ou à Revenir à l'aide de la méthode UpdateTarget () dans l'interface utilisateur. 
  • Ensuite, ajoutez une méthode à la VM qui confirme si le changement est autorisé (cette méthode peut contenir un service qui invite l'utilisateur à confirmer et renvoie un bool).

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();
                }
            }
        }
    }
0
Ahmad