【问题标题】:What event handler to use for ComboBox Item Selected (Selected Item not necessarily changed)为 ComboBox Item Selected 使用什么事件处理程序(Selected Item 不一定要更改)
【发布时间】:2021-01-14 07:37:32
【问题描述】:

目标:在选择组合框下拉列表中的项目时发出事件。

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

问题:只要鼠标单击该项目并且该项目是被选中。

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

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

【问题讨论】:

  • 不要在 WPF 中使用事件。创建一个合适的 ViewModel 并改用 DataBinding 和简单的属性。
  • 必须成为一个事件吗? WPF 提供数据绑定,这将允许您将 ComboBox 的 SelectedItem 绑定到视图的 DataContext 属性
  • 您好,感谢您的建议。看到您的评论后,我尝试了这种方法。我所做的是:将 SelectedItem(也尝试过 SelectedIndex)绑定到 ViewModel 中的一个属性,并在 setter 中执行一些如果有“SelectionChanged”事件时我会执行的逻辑。 However, this still could not solve the problem that when item selected is same as before, setter will not be triggered.我可能在这里遗漏了一些东西。
  • 那么,如果选择了相同的项目,您需要运行什么样的逻辑?状态没有改变,那么您的应用程序究竟会反映什么?
  • 未测试但制作项目按钮然后您可以处理按钮单击事件。

标签: c# wpf


【解决方案1】:

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

您需要像这样处理 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;
  }
}

【讨论】:

    【解决方案2】:

    对我来说ComboBox.DropDownClosed Event 做到了。

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 我们如何在处理程序中获得点击的组合框项值(不是选中的...只有点击的)
      • MyBox.SelectedItemMyBox.SelectedValue。正如另一个MSDN 声明PreviewMouseDown 事件“在按下任何鼠标按钮时发生”。 所以,那个时间SelectedItemSelectedValue 已经设置好了。
      • 也可以使用 ComboBox.SelectedItem = ((ComboBoxItem)sender).Content; (特别是如果您在 ComboBox XAML 中设置了 SelectedValuePath)
      【解决方案4】:

      希望以下技巧对您有所帮助。

      你可以绑定两个事件

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

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

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

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

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

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

          【讨论】:

          • 感谢您的及时回复。我没有从组合框中看到这个选项,这是 C# WPF 吗?
          • 我刚刚注意到它是 WPF,在这种情况下,问题已经发布在这里 stackoverflow.com/questions/7738899/…
          • 这是一个有用的帖子。 (我假设在帖子中,使用它们的内存地址检查“相同项目”是否相等,因此区分重复项)但是在我的组合框列表中,没有重复项。我想要的是:如果我第一次选择项目 1。然后我打开下拉菜单,再次选择第1项,就可以触发一个函数了。
          【解决方案7】:

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

          【讨论】:

          • 你为什么要在带有 Wpf 标签的问题上发布这个?
          【解决方案8】:

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

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

          基本思路是在App.xmal.cs中注册一个EventManager,对所有ComboBoxItem进行嗅探PreviewMouseLeftButtonDownEvent,如果选中项与选中项相同,则触发SelectionChangedEvent,即选择是在不更改索引的情况下执行的

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

          【讨论】:

            【解决方案9】:

            这是一个用于附加到 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" />
            

            【讨论】:

              【解决方案10】:

              使用SelectionChangeCommitted(object sender, EventArgs e)事件 here

              【讨论】:

              • 这是 WPF 而不是 Winforms!
              【解决方案11】:

              每个 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...         
              }
              

              【讨论】:

              • 问题是,如果您的项目发生变化,这将不起作用
              【解决方案12】:

              很简单,加 if (e.AddedItems.Count == 0) return;在函数的开头,例如:

                      private  void ComboBox_Symbols_SelectionChanged(object sender, SelectionChangedEventArgs e)
                  {
                      if (e.AddedItems.Count == 0) 
                      return;
                      //Some Other Codes
                  }
              

              【讨论】:

                【解决方案13】:
                private void OnDropDownClosed(object sender, EventArgs e)
                { 
                     if (combobox.SelectedItem == null) return;
                     // Do actions
                }
                

                【讨论】:

                • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
                猜你喜欢
                • 1970-01-01
                • 2020-10-21
                • 1970-01-01
                • 2021-09-13
                • 1970-01-01
                • 1970-01-01
                • 2013-03-17
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多