【问题标题】:How to program "Selection Changing" event in ListBox in WPF?如何在 WPF 的 ListBox 中编写“选择更改”事件?
【发布时间】:2010-05-10 13:13:02
【问题描述】:

我有一个列表框,如果用户尚未完成某些任务,我想防止更改列表框的选择,这是我目前可以提供的最佳解释,在 WinForms 中曾经有选择更改并且它具有可取消的事件争论,我们甚至可以在其中捕获和取消选择。

我以为我会继承列表框并做一些事情,但在内部,Selector 类中的所有功能都被隐藏了,我可以在反射器中看到,但我无法继承和覆盖任何方法!

【问题讨论】:

    标签: wpf listbox selectionchanging


    【解决方案1】:

    我从 ListBox 派生了一个 MyListBox 类,并添加了名为 SelectionChanging 的事件,它是可取消的事件。然后我在 MyListBox 中使用 MyListBoxItem 作为 ItemContainer,它处理 Preview Left Mouse Up 事件并引发选择更改事件,在取消值时,我将事件标记为已处理,这可以防止新的选择以及它允许​​我通知用户做某事。

    【讨论】:

      【解决方案2】:

      IsSelected绑定到您的视图模型类中的一个属性,并在属性的setter中处理这种情况,例如:

      public bool IsSelected
      {
         get { return _IsSelected; }
         set
         {
             if (value && DisableSelection)
             {
                AlertUser();
             }
             else
             {
                _IsSelected = value;
             }
             OnPropertyChanged("IsSelected");
         }
      }
      

      请注意,即使属性没有更改,您也会引发 PropertyChanged 事件,因为从视图的角度来看,它确实发生了更改。

      【讨论】:

      • 这不起作用,因为 ListBox 已经显示了新的选择,这是我们想要阻止的,我没有使用 MVVM。
      • 哦,那你要做的是处理ListBox上的PreviewMouseDown事件,并将Handled设置为true
      • 是的,我做了同样的事情,但它看起来像是一种解决方法,但是我做了不同的事情,我有自己的从 ListBox 派生的自定义控件,并且有我自己的容器项来处理鼠标按下并引发选择更改事件。
      【解决方案3】:

      我知道这并不能直接回答您的问题,但在大多数情况下(我需要有理由不这样做),在满足选择标准之前,我根本无法控制。

      这个简单的步骤消除了确定值是否已更改以及是否为有效更改等的大部分复杂性。如果您允许编辑组合框(即输入的值),则会增加另一个级别复杂性。

      否则,这里有一个相关的讨论: How to prevent/cancel a combobox's value change in c#?

      【讨论】:

      • 到目前为止我一直在使用完全相同的解决方案,但它不是很好,现在我的代码变得如此复杂,它给我带来了越来越多的问题,所以我想我会尝试其他更好的方法,无论如何谢谢你回答。
      • 从提供的信息中很难判断您的设计。但是,在大多数情况下,您可以将控件的 IsEnabled 属性绑定到代码隐藏中的某个属性,无论是在 Window 的类中,还是(更好)在 MVVM 应用程序的 ViewModel 中。然后,您定义的这个属性将由您的业务规则简单地定义,即您何时可以更改该组合框中的选择。无论如何,您必须在某些时候定义这些规则,并且在大多数情况下,提前定义这些规则比验证控件更容易。 [在下一条评论中继续]
      • [第二部分]换句话说,可能更容易说“我已经完成了 A 和 B,所以现在我可以做 C”,而不是“我正在尝试做 C。我完成了A?是的,我也完成了B吗?没有?所以撤消C。如果您的代码因此而变得过于复杂,请退后一步,看看设计。根据我的经验,如果你觉得它变得太复杂,你可能是对的。在不考虑代码的情况下再次考虑它 - 如果有帮助,请绘制图片或图表。通常,如果您以这种方式思考它,您会发现清晰,然后您只需对您的图片进行编码。希望这是有道理的,并有助于...
      • MVVM 没有帮助,它很好,但我不能在我的应用程序中使用它,其次,即使我尝试使用 MVVM,即使我尝试从 viewmodel 控制,ListBox 也已经显示了选择的新值。但是,我拦截了鼠标事件以触发我的自定义事件,请求取消选择更改,该事件现在有效。
      【解决方案4】:

      一种解决方案是让 ListBox 和 ListBoxItems 不可聚焦,直到您准备好让用户更改它们。这是一个完成此任务的快速模型:

      XAML:

      <StackPanel>
              <ListBox x:Name="LB">
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}"  Content="item 1"/>
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 2"/>
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 3"/>
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 4"/>
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 5"/>
                  <ListBoxItem Focusable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=Focusable}" Content="item 6"/>
              </ListBox>
              <Button Content="Lock/Unlock" Click ="Button_Click"/>
          </StackPanel>
      

      代码:

         private void Button_Click(object sender, RoutedEventArgs e)
          {
              if (LB.Focusable == true)
                  LB.Focusable = false;
              else
                  LB.Focusable = true;
          }
      

      【讨论】:

      • 不错的建议,但不能在我当前的环境中使用它,因为我需要在用户尝试更改它时提醒他们,如果我阻止更改功能,那么他们会认为程序停止了!!
      【解决方案5】:

      这有点小技巧,但我只是使用了列表框的 PreviewMouseLeftButtonDown 事件。

      在处理程序中,我必须询问用户是否确定要离开。出于某种原因,在弹出消息框后,无论您是否将 e.handled 标记为 true,都不会触发所选事件,这与该消息框有关。因此,如果用户确认导航,我将使用鼠标单击来查找被单击的相应数据项(使用 VisualTreeHelper 循环,直到找到 ListBoxItem),然后手动选择该项目。如果用户选择不离开,只需将 e.handled 设置为 false(尽管弹出消息框似乎具有相同的效果。)

      【讨论】:

        【解决方案6】:

        当某些条件不满足时,我也有同样的需求(询问用户是否真的想更改 单选 ListBox 中的选定项目)。单选很重要,否则代码会变得更复杂。我的解决方案基于 tempy 的回答,但是遍历可视化树似乎太麻烦了,所以我只是使用 listBox.ItemContainerGenerator 来询问 ListBox 项目是否鼠标悬停在其中任何一个项目上,并采取相应措施:

            private void listBox_PreviewMouseDown(object sender, MouseButtonEventArgs e) {
                if (CheckIfCurrentlySelectedItemCanBeDeselected())
                    // let things run normally
                    return;
        
                var itemUnderMouse = GetListBoxItemUnderMouse(listBox);
        
                // check if there is no item under mouse
                // or if it is the currently selected item
                if (itemUnderMouse == null || itemUnderMouse.Content == currentItem)
                    return;
        
                // always set Handled and take care of changing selection manually
                e.Handled = true;
        
                if (MessageBox.Show("The selected value is not valid, change selection?", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes) {
                    // change the value manually
                    lbArticles.SelectedItem = itemUnderMouse.Content;
                }
            }
        
            private ListBoxItem GetListBoxItemUnderMouse(ListBox lb) {
                foreach (object o in lb.Items) {
                    ListBoxItem lbi = lb.ItemContainerGenerator.ContainerFromItem(o) as ListBoxItem;
        
                    if (lbi != null && lbi.IsMouseOver) {
                        return lbi;
                    }
                }
        
                return null;
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多