2

我需要在 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 更改为 null

  2. 调用 listBox_SelectionChanged

  3. SelectedItem 设置为以前的值

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

谢谢,任何建议将不胜感激!

4

1 回答 1

1

找到解决方案。在 ListBox 上处理 PreviewMouseLeftButtonDown 对我来说很好。作为附属财产。

顺便说一句:我可以以某种方式关闭这个问题吗?

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;
                    }
                }
            }
        }
    }
于 2013-06-24T14:04:31.377 回答