53

目标:在选择Combobox下拉列表中的项目时,请发出事件。

问题:但是,使用“SelectionChanged”,如果用户选择与当前正在选择的项目相同的项目,则选择不会更改,因此不会触发此事件。

问题:只要鼠标单击该项目并且该项目被选中,无论所选项目是否更改,我都可以使用哪些其他事件处理程序(或其他方式)来发出事件。

(澄清:问题是当再次选择相同的项目时如何触发“某事”。下拉列表中没有重复。场景:第一次选择项目1,关闭下拉。然后再次打开下拉框并选择触发某些功能时的第 1 项。)

解决方案:目前似乎没有直接的解决方案来做到这一点。但是根据每个单独的项目,可以有一些方法来解决它。(如果确实有好的方法,请更新)。谢谢。

4

13 回答 13

33

我有同样的问题,我终于找到了答案:

您需要像这样处理 SelectionChanged 事件和 DropDownClosed :

在 XAML 中:

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

在 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;
  }
}
于 2014-10-31T13:10:06.257 回答
14

对我来说ComboBox.DropDownClosed,Eve​​nt 做到了。

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;
        }
    }
于 2015-06-16T08:53:13.703 回答
10

您可以使用“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

于 2014-08-15T12:46:50.333 回答
7

我希望您会发现以下技巧对您有所帮助。

您可以绑定两个事件

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

并在 OnDropDownOpened 中强制选定项目为空

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

并对 OnSelectionChanged 中的项目执行您需要的操作。每次打开组合框时都会引发 OnSelectionChanged,但您可以在方法内检查 SelectedItem 是否为空并跳过命令

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (combobox.SelectedItem != null)
        {
           //Do something with the selected item
        }
    }
于 2017-07-05T16:11:50.477 回答
3

对于 UWP(Windows 应用商店)应用程序,以上都不起作用(PointerPressed 不会触发;不存在 Preview、DropDownClosed 或 SelectedIndexChanged 事件)

我不得不求助于覆盖 ComboBox 的透明按钮(但不是它的下拉箭头)。当您按下箭头时,列表会像往常一样下拉,并且组合框的 SelectionChanged 事件会触发。当您单击组合框上的任何其他位置时,将触发透明按钮的单击事件,允许您重新选择组合框的当前值。

一些有效的 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>

一些有效的 C# 代码:

    /// <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);
    }
于 2016-08-12T20:39:07.410 回答
1

你可以试试“ SelectedIndexChanged”,即使选择了同一个项目也会触发事件。

于 2013-06-06T15:41:49.650 回答
1

对于 UWP,我尝试了不同的方法。我扩展了 ComboBox 类,并处理了 ComboBox 上的 SelectionChanged 和 OnKeyUp 事件以及 ComboBoxItems 上的 Tapped 事件。如果我在没有首先获得 SelectionChanged 的​​情况下获得 Tapped 事件或 Enter 或 Space 键,那么我知道当前项目已被重新选择并相应地做出响应。

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;
    }
}
于 2019-01-24T22:39:54.160 回答
0

这是一个用于附加到 ComboBox 的 DependencyObject。

它在打开下拉菜单时记录当前选择的项目,然后在关闭下拉菜单时如果仍然选择相同的索引,则触发 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" />
于 2015-04-15T13:24:26.307 回答
0

这个问题困扰了我很长时间,因为没有一种解决方法对我有用:(

但好消息是,以下方法适用于我的应用程序。

基本思路是注册一个EventManagerin对 allApp.xmal.cs进行嗅探,如果选择项与被选择项相同则触发,即不改变 index 进行选择PreviewMouseLeftButtonDownEventComboBoxItemSelectionChangedEvent

App.xmal.cs

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        // raise selection change event even when there's 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:)

于 2014-11-14T23:07:29.183 回答
0

在此处使用SelectionChangeCommitted(object sender, EventArgs e)事件

于 2016-07-09T01:28:03.503 回答
0

每个 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...         
}
于 2018-01-11T16:15:18.843 回答
0

很简单,只要加上 if (e.AddedItems.Count == 0) return; 在功能的开始,例如:

        private  void ComboBox_Symbols_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (e.AddedItems.Count == 0) 
        return;
        //Some Other Codes
    }
于 2020-02-04T13:02:38.870 回答
-1
private void OnDropDownClosed(object sender, EventArgs e)
{ 
     if (combobox.SelectedItem == null) return;
     // Do actions
}
于 2021-01-13T23:37:32.787 回答