WPF ListView拖动选择多个项目

11

想知道是否有人知道如何实现这一功能。我希望能够让用户通过点击和拖动鼠标(不释放点击)来选择多个项目。比如说,用户点击了第一个项目,然后向下拖动到第十个项目;这样第一个到第十个项目就会被选中,就像用户先点击第一个项目,然后按住 Shift 键点击第十个项目一样。

请告诉我,谢谢!


我解决了,稍后我会发布解决方案。谢谢。 - Carlo
我已经在WPF Drag & drop from ListBox with SelectionMode Multiple上发布了一个新的解决方案。 - David Schmitt
3个回答

13

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

XAML:


```html //请将翻译内容放在这里 ```
<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中,这样就可以搞定一切了。

谢谢!


6

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

演示: 演示

1
谢谢!这几乎是一个复制粘贴的过程,除了生成错误的更改命名空间(以及在开头的小节"public partial class Window1 : Window")。 - TripleAntigen

2

需要注意的是,如果用户在鼠标按下之前按住SHIFT键,并且SelectionMode=Extended已经设置,这已经是可能的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接