【问题标题】:wpf listview drag select multiple itemswpf listview拖动选择多个项目
【发布时间】:2010-05-19 21:30:18
【问题描述】:

只是想知道是否有人知道如何做到这一点。我想让用户通过单击并拖动鼠标来选择多个项目(不让点击离开)。假设用户点击第 1 项,然后向下拖动到第 10 项;项目 1 到 10 应该被选中,就好像他点击了项目 1,然后 shift + 点击了项目 10。

告诉我谢谢!

【问题讨论】:

标签: .net wpf


【解决方案1】:

好的,这是我的解决方案,我创建了一个助手类来处理 ListView 的 PreviewLeftMouseButtonDown 和 MouseLeftButtonUp,并为 ListViewItems 创建了一个小样式,当鼠标悬停时它表示助手类,因此它可以决定是否选择项目与否(基于是否按下鼠标左键)。无论如何,这是完整的项目:

XAML:

<Window x:Class="DragSelectListBox.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:DragSelectListBox"
    Title="Window1" Height="300" Width="300">
    <Window.Resources>

        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="local:DragSelectionHelper.IsDragSelecting" Value="False" />
            <Style.Triggers>
                <Trigger Property="ListBoxItem.IsMouseOver" Value="True">
                    <Setter Property="local:DragSelectionHelper.IsDragSelecting" Value="True" />
                </Trigger>
            </Style.Triggers>
        </Style>

    </Window.Resources>

    <Grid Background="AliceBlue">
        <ListBox Margin="8"
                 local:DragSelectionHelper.IsDragSelectionEnabled="true">
            <ListBoxItem Content="Item 1" />
            <ListBoxItem Content="Item 2" />
            <ListBoxItem Content="Item 3" />
            <ListBoxItem Content="Item 4" />
            <ListBoxItem Content="Item 5" />
            <ListBoxItem Content="Item 6" />
            <ListBoxItem Content="Item 7" />
            <ListBoxItem Content="Item 8" />
            <ListBoxItem Content="Item 9" />
            <ListBoxItem Content="Item 10" />
            <ListBoxItem Content="Item 11" />
            <ListBoxItem Content="Item 12" />
            <ListBoxItem Content="Item 13" />
            <ListBoxItem Content="Item 14" />
            <ListBoxItem Content="Item 15" />
            <ListBoxItem Content="Item 16" />
            <ListBoxItem Content="Item 17" />
            <ListBoxItem Content="Item 18" />
            <ListBoxItem Content="Item 19" />
            <ListBoxItem Content="Item 20" />
            <ListBoxItem Content="Item 21" />
            <ListBoxItem Content="Item 22" />
            <ListBoxItem Content="Item 23" />
            <ListBoxItem Content="Item 24" />
            <ListBoxItem Content="Item 25" />
            <ListBoxItem Content="Item 26" />
            <ListBoxItem Content="Item 27" />
            <ListBoxItem Content="Item 28" />
            <ListBoxItem Content="Item 29" />
            <ListBoxItem Content="Item 30" />
        </ListBox>
    </Grid>
</Window>

C#:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace DragSelectListBox
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
    }

    // CARLO 20100519: Helper class for DragSelection
    public class DragSelectionHelper : DependencyObject
    {
        #region Random Static Properties

        // need a static reference to the listbox otherwise it can't be accessed
            // (this only happened in the project I'm working on, if you're using a regular ListBox, with regular ListBoxItems you can get the ListBox from the ListBoxItems)
        public static ListBox ListBox { get; private set; }

        #endregion Random Static Properties

        #region IsDragSelectionEnabledProperty

        public static bool GetIsDragSelectionEnabled(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDragSelectionEnabledProperty);
        }

        public static void SetIsDragSelectionEnabled(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDragSelectionEnabledProperty, value);
        }

        public static readonly DependencyProperty IsDragSelectionEnabledProperty =
            DependencyProperty.RegisterAttached("IsDragSelectingEnabled", typeof(bool), typeof(DragSelectionHelper), new UIPropertyMetadata(false, IsDragSelectingEnabledPropertyChanged));

        public static void IsDragSelectingEnabledPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ListBox listBox = o as ListBox;

            bool isDragSelectionEnabled = DragSelectionHelper.GetIsDragSelectionEnabled(listBox);

            // if DragSelection is enabled
            if (isDragSelectionEnabled)
            {
                // set the listbox's selection mode to multiple ( didn't work with extended )
                listBox.SelectionMode = SelectionMode.Multiple;

                // set the static listbox property
                DragSelectionHelper.ListBox = listBox;

                // and subscribe to the required events to handle the drag selection and the attached properties
                listBox.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSelectionHelper.listBox_PreviewMouseLeftButtonDown);
                listBox.PreviewMouseRightButtonDown += new MouseButtonEventHandler(listBox_PreviewMouseRightButtonDown);
                listBox.MouseLeftButtonUp += new MouseButtonEventHandler(DragSelectionHelper.listBox_MouseLeftButtonUp);
            }
            else // is selection is disabled
            {
                // set selection mode to the default
                listBox.SelectionMode = SelectionMode.Single;

                // dereference the listbox
                DragSelectionHelper.ListBox = null;

                // unsuscribe from the events
                listBox.PreviewMouseLeftButtonDown -= new MouseButtonEventHandler(DragSelectionHelper.listBox_PreviewMouseLeftButtonDown);
                listBox.MouseLeftButtonUp -= new MouseButtonEventHandler(DragSelectionHelper.listBox_MouseLeftButtonUp);
                listBox.MouseLeftButtonUp -= new MouseButtonEventHandler(DragSelectionHelper.listBox_MouseLeftButtonUp);
            }
        }

        static void listBox_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            // to prevent the listbox from selecting / deselecting wells on right click
            e.Handled = true;
        }

        private static void listBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // notify the helper class that the listbox has initiated the drag click
            DragSelectionHelper.SetIsDragClickStarted(DragSelectionHelper.ListBox, true);
        }

        private static void listBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            // notify the helper class that the list box has terminated the drag click
            DragSelectionHelper.SetIsDragClickStarted(DragSelectionHelper.ListBox, false);
        }

        #endregion IsDragSelectionEnabledProperty

        #region IsDragSelectinProperty

        public static bool GetIsDragSelecting(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDragSelectingProperty);
        }

        public static void SetIsDragSelecting(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDragSelectingProperty, value);
        }

        public static readonly DependencyProperty IsDragSelectingProperty =
            DependencyProperty.RegisterAttached("IsDragSelecting", typeof(bool), typeof(DragSelectionHelper), new UIPropertyMetadata(false, IsDragSelectingPropertyChanged));

        public static void IsDragSelectingPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            ListBoxItem item = o as ListBoxItem;

            bool clickInitiated = DragSelectionHelper.GetIsDragClickStarted(DragSelectionHelper.ListBox);

            // this is where the item.Parent was null, it was supposed to be the ListBox, I guess it's null because items are not
            // really ListBoxItems but are wells
            if (clickInitiated)
            {
                bool isDragSelecting = DragSelectionHelper.GetIsDragSelecting(item);

                if (isDragSelecting)
                {
                    // using the ListBox static reference because could not get to it through the item.Parent property
                    DragSelectionHelper.ListBox.SelectedItems.Add(item);
                }
            }
        }

        #endregion IsDragSelectinProperty

        #region IsDragClickStartedProperty

        public static bool GetIsDragClickStarted(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsDragClickStartedProperty);
        }

        public static void SetIsDragClickStarted(DependencyObject obj, bool value)
        {
            obj.SetValue(IsDragClickStartedProperty, value);
        }

        public static readonly DependencyProperty IsDragClickStartedProperty =
            DependencyProperty.RegisterAttached("IsDragClickStarted", typeof(bool), typeof(DragSelectionHelper), new UIPropertyMetadata(false, IsDragClickStartedPropertyChanged));

        public static void IsDragClickStartedPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            bool isDragClickStarted = DragSelectionHelper.GetIsDragClickStarted(DragSelectionHelper.ListBox);

            // if click has been drag click has started, clear the current selected items and start drag selection operation again
            if (isDragClickStarted)
                DragSelectionHelper.ListBox.SelectedItems.Clear();
        }

        #endregion IsDragClickInitiatedProperty
    }
}

如您所见,您需要做的就是在您的 xaml 中添加样式,然后设置:

local:DragSelectionHelper.IsDragSelectionEnabled="true"

ListView 的附加属性,它会处理所有事情。

谢谢!

【讨论】:

    【解决方案2】:

    根据 Carlo 的回答,我修改了他的一些代码,现在您可以使用 CTRL 继续选择而不清除当前选择,并使用 SHIFT 选择类似“扩展”模式的项目。

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Input;
    using System.Windows.Media;
    
    namespace SteamFriendsManager.Utility
    {
        public class DragSelectionHelper : DependencyObject
        {
            #region IsDragSelectionEnabledProperty
    
            public static bool GetIsDragSelectionEnabled(DependencyObject obj)
            {
                return (bool) obj.GetValue(IsDragSelectionEnabledProperty);
            }
    
            public static void SetIsDragSelectionEnabled(DependencyObject obj, bool value)
            {
                obj.SetValue(IsDragSelectionEnabledProperty, value);
            }
    
            public static readonly DependencyProperty IsDragSelectionEnabledProperty =
                DependencyProperty.RegisterAttached("IsDragSelectingEnabled", typeof (bool), typeof (DragSelectionHelper),
                    new UIPropertyMetadata(false, IsDragSelectingEnabledPropertyChanged));
    
            private static void IsDragSelectingEnabledPropertyChanged(DependencyObject o,
                DependencyPropertyChangedEventArgs e)
            {
                var listBox = o as ListBox;
    
                if (listBox == null)
                    return;
    
                // if DragSelection is enabled
                if (GetIsDragSelectionEnabled(listBox))
                {
                    // set the listbox's selection mode to multiple ( didn't work with extended )
                    listBox.SelectionMode = SelectionMode.Multiple;
    
                    // and subscribe to the required events to handle the drag selection and the attached properties
                    listBox.PreviewMouseRightButtonDown += listBox_PreviewMouseRightButtonDown;
    
                    listBox.PreviewMouseLeftButtonDown += listBox_PreviewMouseLeftButtonDown;
                    listBox.PreviewMouseLeftButtonUp += listBox_PreviewMouseLeftButtonUp;
    
                    listBox.PreviewKeyDown += listBox_PreviewKeyDown;
                    listBox.PreviewKeyUp += listBox_PreviewKeyUp;
                }
                else // is selection is disabled
                {
                    // set selection mode to the default
                    listBox.SelectionMode = SelectionMode.Extended;
    
                    // unsuscribe from the events
                    listBox.PreviewMouseRightButtonDown -= listBox_PreviewMouseRightButtonDown;
    
                    listBox.PreviewMouseLeftButtonDown -= listBox_PreviewMouseLeftButtonDown;
                    listBox.PreviewMouseLeftButtonUp -= listBox_PreviewMouseLeftButtonUp;
    
                    listBox.PreviewKeyDown -= listBox_PreviewKeyDown;
                    listBox.PreviewKeyUp += listBox_PreviewKeyUp;
                }
            }
    
            private static void listBox_PreviewKeyDown(object sender, KeyEventArgs e)
            {
                var listBox = sender as ListBox;
                if (listBox == null)
                    return;
    
                if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
                {
                    SetIsDragSelectionEnabled(listBox, false);
                }
            }
    
            private static void listBox_PreviewKeyUp(object sender, KeyEventArgs e)
            {
                var listBox = sender as ListBox;
                if (listBox == null)
                    return;
    
                if (e.Key == Key.LeftShift || e.Key == Key.RightShift)
                {
                    SetIsDragSelectionEnabled(listBox, true);
                }
            }
    
            private static void listBox_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
            {
                // to prevent the listbox from selecting / deselecting wells on right click
                e.Handled = true;
            }
    
            private static void listBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
            {
                SetIsDragClickStarted(sender as DependencyObject, true);
            }
    
            private static void listBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
            {
                SetIsDragClickStarted(sender as DependencyObject, false);
            }
    
            public static DependencyObject GetParent(DependencyObject obj)
            {
                if (obj == null)
                    return null;
    
                var ce = obj as ContentElement;
                if (ce == null) return VisualTreeHelper.GetParent(obj);
    
                var parent = ContentOperations.GetParent(ce);
                if (parent != null)
                    return parent;
    
                var fce = ce as FrameworkContentElement;
                return fce != null ? fce.Parent : null;
            }
    
            #endregion IsDragSelectionEnabledProperty
    
            #region IsDragSelectingProperty
    
            public static bool GetIsDragSelecting(DependencyObject obj)
            {
                return (bool) obj.GetValue(IsDragSelectingProperty);
            }
    
            public static void SetIsDragSelecting(DependencyObject obj, bool value)
            {
                obj.SetValue(IsDragSelectingProperty, value);
            }
    
            public static readonly DependencyProperty IsDragSelectingProperty =
                DependencyProperty.RegisterAttached("IsDragSelecting", typeof (bool), typeof (DragSelectionHelper),
                    new UIPropertyMetadata(false, IsDragSelectingPropertyChanged));
    
            private static void IsDragSelectingPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
            {
                var listBoxItem = o as ListBoxItem;
    
                if (listBoxItem == null)
                    return;
    
                if (!GetIsDragClickStarted(listBoxItem)) return;
    
                if (GetIsDragSelecting(listBoxItem))
                {
                    listBoxItem.IsSelected = true;
                }
            }
    
            #endregion IsDragSelectingProperty
    
            #region IsDragClickStartedProperty
    
            public static bool GetIsDragClickStarted(DependencyObject obj)
            {
                return (bool) obj.GetValue(IsDragClickStartedProperty);
            }
    
            public static void SetIsDragClickStarted(DependencyObject obj, bool value)
            {
                obj.SetValue(IsDragClickStartedProperty, value);
            }
    
            public static readonly DependencyProperty IsDragClickStartedProperty =
                DependencyProperty.RegisterAttached("IsDragClickStarted", typeof (bool), typeof (DragSelectionHelper),
                    new FrameworkPropertyMetadata(false, IsDragClickStartedPropertyChanged) {Inherits = true});
    
            private static void IsDragClickStartedPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
            {
                var listBox = obj as ListBox;
    
                if (listBox == null)
                    return;
    
                if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
                    return;
    
                var hitTestResult = VisualTreeHelper.HitTest(listBox, Mouse.GetPosition(listBox));
                if (hitTestResult == null)
                    return;
    
                var element = hitTestResult.VisualHit;
                while (element != null)
                {
                    var scrollBar = element as ScrollBar;
                    if (scrollBar != null)
                    {
                        return;
                    }
                    element = VisualTreeHelper.GetParent(element);
                }
    
                if (GetIsDragClickStarted(listBox))
                    listBox.SelectedItems.Clear();
            }
    
            #endregion IsDragClickInitiatedProperty
        }
    }
    

    演示:

    【讨论】:

    • 谢谢!这几乎是一个复制和粘贴,除了生成错误的更改命名空间(以及开头的小部分“公共部分类 Window1:Window”)
    【解决方案3】:

    另请注意,如果用户在按下鼠标按钮之前按住 SHIFT 键,则 SelectionMode=Extended 已经可以做到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-16
      • 1970-01-01
      • 2015-08-07
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多