防止取消选择列表框中最后一个选定项的最佳方式

本文关键字:方式 最佳 最后一个 取消 选择 列表 | 更新日期: 2023-09-27 18:19:45

我需要在wpf中为ListBox控件实现自定义行为。其想法是禁用取消选择最后一个选定的元素。根据默认行为,当用户用鼠标点击所选项目时,按住ctrl键,选择将消失。我需要实现一些逻辑,当用户在最后一个选择的项目上点击鼠标+ctrl时,使列表框什么都不做。

我找到的唯一方法是订阅ListBox.SelectionChanged并执行以下操作:

private static void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            var listBox = sender as ListBox;
            if (listBox != null && e.RemovedItems != null && e.RemovedItems.Count == 1)
            {
                var removed = e.RemovedItems[0];
                if (listBox.SelectedItems.Count == 0)
                {
                    if (listBox.SelectionMode == System.Windows.Controls.SelectionMode.Single)
                    {
                        listBox.SelectedItem = removed;
                    }
                    else
                    {
                        listBox.SelectedItems.Add(removed);
                    }
                    e.Handled = true;
                }
            }
        }

但这种解决方案不适合我,因为在这种情况下,当ListBox.SelectedItem绑定到viewmodel属性时,会发生一些不需要的调用。

伪调用堆栈(取消选择所选项目时):

  1. SelectedItem更改为空

  2. listBox_SelectionChanged称为

  3. SelectedItem设置为以前的值

我只希望第1步和第3步永远不会发生。这一点很重要,因为当SelectedItem更改时,会启动一些长时间运行的异步操作。

谢谢,如有任何建议,不胜感激!

防止取消选择列表框中最后一个选定项的最佳方式

找到解决方案。处理ListBox上的PreviewMouseLeftButtonDown对我来说很好。作为附加属性。

BTW:我能以某种方式解决这个问题吗?

public static class ListBoxAttachedProperties
    {
        public static readonly DependencyProperty DisableUnselectLast = 
            DependencyProperty.RegisterAttached(
            "DisableUnselectLast", typeof(bool), typeof(ListBox),
            new PropertyMetadata(false, DisableUnselectLastChangedCallback));
        public static bool GetDisableUnselectLast(DependencyObject d)
        {
            return (bool)d.GetValue(DisableUnselectLast);
        }
        public static void SetDisableUnselectLast(DependencyObject d, bool value)
        {
            d.SetValue(DisableUnselectLast, value);
        }
        private static void DisableUnselectLastChangedCallback(
            DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ListBox))
            {
                return;
            }
            var selector = d as ListBox;
            bool oldValue = (bool)e.OldValue;
            bool newValue = (bool)e.NewValue;
            if (oldValue == newValue)
            {
                return;
            }
            if (oldValue == false)
            {
                selector.PreviewMouseLeftButtonDown += listBox_PreviewMouseLeftButtonDown;
            }
            else
            {
                selector.PreviewMouseLeftButtonDown -= listBox_PreviewMouseLeftButtonDown;
            }
        }
        private static void listBox_PreviewMouseLeftButtonDown(
            object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            var listBox = sender as ListBox;
            if (listBox != null && listBox.SelectedItems.Count == 1)
            {
                UIElement container = listBox.ItemContainerGenerator
                    .ContainerFromItem(listBox.SelectedItems[0]) as UIElement;
                if (container != null)
                {
                    var pos = e.GetPosition(container);
                    var result = VisualTreeHelper.HitTest(container, pos);
                    if (result != null)
                    {
                        e.Handled = true;
                    }
                }
            }
        }
    }