web-dev-qa-db-fra.com

Quel gestionnaire d'événements à utiliser pour l'élément ComboBox sélectionné (l'élément sélectionné n'est pas nécessairement modifié)

Objectif: émettre un événement lorsque les éléments d'une liste déroulante sont sélectionnés.

Problème: Utilisation de "SelectionChanged". Cependant, si l'utilisateur choisit le même élément que celui en cours de sélection, la sélection ne sera pas modifiée et donc cet événement ne sera pas déclenché.

Question: Quel autre gestionnaire d'événement (ou d'autres moyens) je peux utiliser pour émettre un événement, que l'élément sélectionné soit modifié ou non, tant que la souris a cliqué sur cet élément et que cet élément est sélectionné.

(Précision: le problème est de savoir comment déclencher "quelque chose" lorsque le même élément est sélectionné à nouveau. Aucun doublon dans la liste déroulante. Scénario: sélectionnez pour la première fois l'élément 1, fermez le menu déroulant. élément 1 lorsque certaines fonctions sont déclenchées.)

Solution : pour le moment, il ne semble pas exister de solution simple pour le faire. Mais selon chaque projet, il peut exister des moyens de le contourner. (S'il vous plaît mettre à jour s'il existe effectivement de bonnes façons de le faire). Merci.

31
grc

J'avais la même question et j'ai finalement trouvé la réponse:

Vous devez gérer les événements SelectionChanged et DropDownClosed de la manière suivante:

En XAML:

<ComboBox Name="cmbSelect" SelectionChanged="ComboBox_SelectionChanged" DropDownClosed="ComboBox_DropDownClosed">
    <ComboBoxItem>1</ComboBoxItem>
    <ComboBoxItem>2</ComboBoxItem>
    <ComboBoxItem>3</ComboBoxItem>
</ComboBox>

En C #:

private bool handle = true;
private void ComboBox_DropDownClosed(object sender, EventArgs e) {
  if(handle)Handle();
  handle = true;
}

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  ComboBox cmb = sender as ComboBox;
  handle = !cmb.IsDropDownOpen;
  Handle();
}

private void Handle() {
  switch (cmbSelect.SelectedItem.ToString().Split(new string[] { ": " }, StringSplitOptions.None).Last())
  { 
      case "1":
          //Handle for the first combobox
          break;
      case "2":
          //Handle for the second combobox
          break;
      case "3":
          //Handle for the third combobox
          break;
  }
}
15
deltonio2

Vous pouvez utiliser l'événement "ComboBoxItem.PreviewMouseDown". Ainsi, chaque fois que la souris est enfoncée sur un élément, cet événement est déclenché.

Pour ajouter cet événement en XAML, utilisez "ComboBox.ItemContainerStyle" comme dans l'exemple suivant:

   <ComboBox x:Name="MyBox"
        ItemsSource="{Binding MyList}"
        SelectedValue="{Binding MyItem, Mode=OneWayToSource}" >
        <ComboBox.ItemContainerStyle>
            <Style>
                <EventSetter Event="ComboBoxItem.PreviewMouseDown"
                    Handler="cmbItem_PreviewMouseDown"/>
            </Style>
        </ComboBox.ItemContainerStyle>
   </ComboBox>

et le gérer comme d'habitude

void cmbItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    //...do your item selection code here...
}

Merci à MSDN

6
Vadim Levkovsky

Pour moi, ComboBox.DropDownClosed Event l'a fait.

private void cbValueType_DropDownClosed(object sender, EventArgs e)
    {
        if (cbValueType.SelectedIndex == someIntValue) //sel ind already updated
        {
            // change sel Index of other Combo for example
            cbDataType.SelectedIndex = someotherIntValue;
        }
    }
5
Daniel

J'espère que vous trouverez utile le truc suivant.

Vous pouvez lier les deux événements

combobox.SelectionChanged += OnSelectionChanged;
combobox.DropDownOpened += OnDropDownOpened;

Et forcer l'élément sélectionné à null à l'intérieur du OnDropDownOpened

private void OnDropDownOpened(object sender, EventArgs e)
{
    combobox.SelectedItem = null;
}

Et faites ce dont vous avez besoin avec l'élément dans OnSelectionChanged . OnSelectionChanged sera levé à chaque fois que vous ouvrirez la liste déroulante, mais vous pouvez vérifier si SelectedItem est null dans la méthode et ignorer la commande

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (combobox.SelectedItem != null)
        {
           //Do something with the selected item
        }
    }
4
Gianluca Conte

Pour les applications UWP (Windows Store), aucune de ces solutions ne fonctionnera (PointerPressed ne se déclenche pas; aucun événement Preview, DropDownClosed ou SelectedIndexChanged n'existe)

J'ai dû recourir à un bouton transparent superposant le ComboBox (mais pas sa flèche déroulante). Lorsque vous appuyez sur la flèche, la liste défile normalement et l'événement SelectionChanged de la zone de liste déroulante est déclenché. Lorsque vous cliquez n'importe où sur la zone de liste déroulante, l'événement de clic du bouton transparent se déclenche, ce qui vous permet de sélectionner à nouveau la valeur actuelle de la zone de liste déroulante.

Quelques codes XAML de travail:

                <Grid x:Name="ComboOverlay" Margin="0,0,5,0"> <!--See comments in code behind at ClickedComboButValueHasntChanged event handler-->
                    <ComboBox x:Name="NewFunctionSelect" Width="97" ItemsSource="{x:Bind Functions}"
                          SelectedItem="{x:Bind ChosenFunction}" SelectionChanged="Function_SelectionChanged"/>
                    <Button x:Name="OldFunctionClick" Height="30" Width="73" Background="Transparent" Click="ClickedComboButValueHasntChanged"/>
                </Grid>

Quelques codes C # de travail:

    /// <summary>
    /// It is impossible to simply click a ComboBox to select the shown value again. It always drops down the list of options but
    ///     doesn't raise SelectionChanged event if the value selected from the list is the same as before
    ///     
    /// To handle this, a transparent button is overlaid over the ComboBox (but not its dropdown arrow) to allow reselecting the old value
    /// Thus clicking over the dropdown arrow allows the user to select a new option from the list, but
    ///     clicking anywhere else in the Combo re-selects the previous value
    /// </summary>
    private void ClickedComboButValueHasntChanged(object sender, RoutedEventArgs e)
    {
        //You could also dummy up a SelectionChangedEvent event and raise it to invoke Function_SelectionChanged handler, below 
        FunctionEntered(NewFunctionSelect.SelectedValue as string);
    }

    private void Function_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        FunctionEntered(e.AddedItems[0] as string);
    }
2
Robert

Vous pouvez essayer "SelectedIndexChanged", l'événement sera déclenché même si le même élément est sélectionné.

1
Vas Vasanth

Utilisez SelectionChangeCommitted(object sender, EventArgs e) event here

0
Zack

Ceci est un DependencyObject pour attacher à un ComboBox.

Il enregistre l'élément actuellement sélectionné lorsque le menu déroulant est ouvert, puis déclenche l'événement SelectionChanged si le même index est toujours sélectionné à la fermeture du menu déroulant. Il faudra peut-être le modifier pour qu'il fonctionne avec la sélection au clavier.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Web.UI.WebControls;

namespace MyNamespace 
{
    public class ComboAlwaysFireSelection : DependencyObject
    {
        public static readonly DependencyProperty ActiveProperty = DependencyProperty.RegisterAttached(
            "Active",
            typeof(bool),
            typeof(ComboAlwaysFireSelection),
            new PropertyMetadata(false, ActivePropertyChanged));

        private static void ActivePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var element = d as ComboBox;
            if (element == null) 
                return;

            if ((e.NewValue as bool?).GetValueOrDefault(false))
            {
                element.DropDownClosed += ElementOnDropDownClosed;
                element.DropDownOpened += ElementOnDropDownOpened;
            }
            else
            {
                element.DropDownClosed -= ElementOnDropDownClosed;
                element.DropDownOpened -= ElementOnDropDownOpened;
            }
        }

        private static void ElementOnDropDownOpened(object sender, EventArgs eventArgs)
        {
            _selectedIndex = ((ComboBox) sender).SelectedIndex;
        }

        private static int _selectedIndex;

        private static void ElementOnDropDownClosed(object sender, EventArgs eventArgs)
        {
            var comboBox = ((ComboBox) sender);
            if (comboBox.SelectedIndex == _selectedIndex)
            {
                comboBox.RaiseEvent(new SelectionChangedEventArgs(Selector.SelectionChangedEvent, new ListItemCollection(), new ListItemCollection()));
            }
        }

        [AttachedPropertyBrowsableForChildrenAttribute(IncludeDescendants = false)]
        [AttachedPropertyBrowsableForType(typeof(ComboBox))]
        public static bool GetActive(DependencyObject @object)
        {
            return (bool)@object.GetValue(ActiveProperty);
        }

        public static void SetActive(DependencyObject @object, bool value)
        {
            @object.SetValue(ActiveProperty, value);
        }
    }
}

et ajoutez votre préfixe d'espace de noms pour le rendre accessible.

<UserControl xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.Microsoft.com/expression/blend/2008" 
             xmlns:ut="clr-namespace:MyNamespace" ></UserControl>

et puis vous devez l'attacher comme si

<ComboBox ut:ComboAlwaysFireSelection.Active="True" />
0
jv_

Chaque instance de ComboBoxItem a un événement PreviewMouseDown. Si vous souhaitez abonner votre gestionnaire personnalisé à cet événement sur chaque ComboBoxItem, vous aurez la possibilité de gérer chaque clic sur la liste déroulante.

// Subscribe on ComboBoxItem-s events.
comboBox.Items.Cast<ComboBoxItem>().ToList().ForEach(i => i.PreviewMouseDown += ComboBoxItem_PreviewMouseDown);

private void ComboBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
     // your handler logic...         
}
0

Pour UWP, j'ai essayé une approche différente. J'ai étendu la classe ComboBox et traité les événements SelectionChanged et OnKeyUp sur la ComboBox ainsi que l'événement Tapped sur la ComboBoxItems. Dans les cas où je reçois un événement tapé ou une touche Entrée ou Espace sans obtenir au préalable un SelectionChanged, je sais que l'élément en cours a été re-sélectionné et je réponds en conséquence.

class ExtendedComboBox : ComboBox
{
    public ExtendedComboBox()
    {
        SelectionChanged += OnSelectionChanged;
    }

    protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
    {
        ComboBoxItem cItem = element as ComboBoxItem;
        if (cItem != null)
        {
            cItem.Tapped += OnItemTapped;
        }

        base.PrepareContainerForItemOverride(element, item);
    }

    protected override void OnKeyUp(KeyRoutedEventArgs e)
    {
        // if the user hits the Enter or Space to select an item, then consider this a "reselect" operation
        if ((e.Key == Windows.System.VirtualKey.Space || e.Key == Windows.System.VirtualKey.Enter) && !isSelectionChanged)
        {
            // handle re-select logic here
        }

        isSelectionChanged = false;

        base.OnKeyUp(e);
    }

    // track whether or not the ComboBox has received a SelectionChanged notification
    // in cases where it has not yet we get a Tapped or KeyUp notification we will want to consider that a "re-select"
    bool isSelectionChanged = false;
    private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        isSelectionChanged = true;
    }

    private void OnItemTapped(object sender, TappedRoutedEventArgs e)
    {
        if (!isSelectionChanged)
        {
            // indicates that an item was re-selected  - handle logic here
        }

        isSelectionChanged = false;
    }
}
0
Rich S