Подтвердить что ты не робот

Какой обработчик события использовать для выбранного компонента ComboBox (выбранный элемент необязательно изменен)

Цель: выдать событие, когда выбраны элементы в выпадающем списке combobox.

Проблема: Использование "SelectionChanged", однако, если пользователь выбирает тот же элемент, что и элемент, который в данный момент выбран, выбор не изменяется и, следовательно, это событие не будет запущено.

Вопрос: Какой другой обработчик событий (или другие способы), который я могу использовать для выдачи события независимо от выбранного элемента, изменяется или не до тех пор, пока щелчок мышью по этому элементу и этот элемент выбирается.

(Уточнение: проблема заключается в том, как вызывать "что-то", когда один и тот же элемент снова выбирается. В раскрывающемся списке нет дубликатов. Сценарий: первый раз выберите элемент 1, закройте раскрывающийся список. Затем снова откройте раскрывающийся список и выберите элемент 1 при срабатывании какой-либо функции.)

Решение: на данный момент, похоже, нет прямого решения для этого. Но в соответствии с каждым отдельным проектом могут быть способы обойти его. (Пожалуйста, обновите, если есть действительно хорошие способы сделать это). Спасибо.

4b9b3361

Ответ 1

У меня был тот же вопрос, и я наконец нашел ответ:

Вам нужно обработать BOTH событие SelectionChanged и DropDownClosed следующим образом:

В XAML:

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

В С#:

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

Ответ 2

Вы можете использовать событие "ComboBoxItem.PreviewMouseDown". Поэтому каждый раз, когда мышь на каком-то элементе, это событие будет запущено.

Чтобы добавить это событие в XAML, используйте "ComboBox.ItemContainerStyle", как в следующем примере:

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

и обрабатывать его как обычно

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

Благодаря MSDN

Ответ 3

Для меня ComboBox.DropDownClosed Событие сделало это.

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

Ответ 4

Я надеюсь, что вы найдете полезный трюк.

Вы можете связать оба события

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

И заставьте выбранный элемент в значение null внутри OnDropDownOpened

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

И сделайте то, что вам нужно, с элементом внутри OnSelectionChanged. OnSelectionChanged будет подниматься каждый раз, когда вы откроете combobox, но вы можете проверить, является ли SelectedItem нулевым внутри метода и пропустить команду

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

Ответ 5

Для приложений UWP (Windows Store) ни одно из вышеперечисленных действий не будет работать (PointerPressed не запускается, не существует событий Preview, DropDownClosed или SelectedIndexChanged)

Мне пришлось прибегнуть к прозрачной кнопке, накладывающейся на ComboBox (но не на стрелку вниз). Когда вы нажимаете на стрелку, список падает, как обычно, и происходит событие Combo Box SelectionChanged. Когда вы нажимаете в другом месте в Combo Box, прозрачная кнопка запускает событие, позволяя вам повторно выбрать текущее значение Combo Box.

Некоторые рабочие коды XAML:

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

Некоторый рабочий код С#:

    /// <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);
    }

Ответ 6

Вы можете попробовать "SelectedIndexChanged", это вызовет событие, даже если выбран тот же самый элемент.

Ответ 7

Эта проблема часто меня беспокоит, так как ни одна работа не работала для меня: (

Но хорошая новость заключается в следующем методе, который отлично подходит для моего приложения.

Основная идея - зарегистрировать EventManager в App.xmal.cs для sniff PreviewMouseLeftButtonDownEvent для всех ComboBoxItem, затем запустить SelectionChangedEvent, если элемент выбора совпадает с выбранным элементом, то есть выбор выполняется без изменения индекса.

В App.xmal.cs:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // raise selection change event even when there no change in index
        EventManager.RegisterClassHandler(typeof(ComboBoxItem), UIElement.PreviewMouseLeftButtonDownEvent,
                                          new MouseButtonEventHandler(ComboBoxSelfSelection), true);

        base.OnStartup(e);
    }

    private static void ComboBoxSelfSelection(object sender, MouseButtonEventArgs e)
    {
        var item = sender as ComboBoxItem;

        if (item == null) return;

        // find the combobox where the item resides
        var comboBox = ItemsControl.ItemsControlFromItemContainer(item) as ComboBox;

        if (comboBox == null) return;

        // fire SelectionChangedEvent if two value are the same
        if ((string)comboBox.SelectedValue == (string)item.Content)
        {
            comboBox.IsDropDownOpen = false;
            comboBox.RaiseEvent(new SelectionChangedEventArgs(Selector.SelectionChangedEvent, new ListItem(), new ListItem()));
        }
    }
}

Затем для всех комбинированных полей зарегистрируйте SelectionChangedEvent обычным способом:

<ComboBox ItemsSource="{Binding BindList}"
          SelectionChanged="YourSelectionChangedEventHandler"/>

Теперь, если два индекса отличаются друг от друга, ничего особенного, кроме обычного процесса обработки событий; если два индекса совпадают, сначала будет обрабатываться событие мыши на элементе, и таким образом запускается SelectionChangedEvent. Таким образом, обе ситуации вызовут SelectionChangedEvent:)

Ответ 8

Это объект DependencyObject для присоединения к ComboBox.

Он записывает текущий выбранный элемент, когда раскрывающийся список открывается, а затем запускает событие SelectionChanged, если тот же индекс все еще выбран, когда выпадающее меню закрыто. Возможно, потребуется изменить его для работы с выбором клавиатуры.

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);
        }
    }
}

и добавьте префикс пространства имен, чтобы сделать его доступным.

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

а затем вам нужно прикрепить его так

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

Ответ 9

Использовать событие SelectionChangeCommitted(object sender, EventArgs e) здесь

Ответ 10

Каждый экземпляр ComboBoxItem имеет событие PreviewMouseDown. Если вы подпишете свой пользовательский обработчик на это событие для каждого ComboBoxItem, у вас будет возможность обрабатывать каждый щелчок в раскрывающемся списке.

// 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...         
}

Ответ 11

Для UWP я попробовал другой подход. Я расширил класс ComboBox и обработал события SelectionChanged и OnKeyUp в ComboBox, а также событие Tapped в ComboBoxItems. В тех случаях, когда я получаю событие Tapped или клавишу Enter или пробел без предварительного получения SelectionChanged, я знаю, что текущий элемент был повторно выбран, и я отвечаю соответствующим образом.

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