WPF 通过拖拽改变Item的顺序 ItemsControl

  • Post author:
  • Post category:其他


拖拽每个元素 移动位置 和点击前后移动位置  要求低一点就点击前后移动位置就够了

之前发过一篇 WPF,strackpanel 拖拽控件 改名,实际上是差不多的,随着对此深入理解可以更加的封装


https://blog.csdn.net/sinat_30224769/article/details/111668459?spm=1001.2014.3001.5501

git链接


https://github.com/442040292/ItemsControlDragSortDemo

基础代码重要

扩展方法

    internal static partial class ItemsControlDropSortExtendMethod
    {
        //根据子元素查找父元素
        public static T FindVisualParent<T>(DependencyObject obj) where T : class
        {
            while (obj != null)
            {
                if (obj is T)
                    return obj as T;

                obj = VisualTreeHelper.GetParent(obj);
            }
            return null;
        }

        public static bool GetDragItem(this MouseEventArgs e, object sender, out object data)
        {
            data = null;
            var pos = e.GetPosition((IInputElement)sender);
            HitTestResult result = VisualTreeHelper.HitTest((Visual)sender, pos);
            if (result == null)
            {
                return false;
            }
            var listBoxItem = ItemsControlDropSortExtendMethod.FindVisualParent<FrameworkElement>(result.VisualHit);
            if (listBoxItem == null)
            {
                return false;
            }
            if (listBoxItem.DataContext == null)
            {
                return false;
            }
            data = listBoxItem.DataContext;
            return true;
        }

        public static bool GetDropItem(this DragEventArgs e, object sender, out object from, out object to)
        {
            from = null;
            to = null;
            var pos = e.GetPosition((IInputElement)sender);
            var result = VisualTreeHelper.HitTest((Visual)sender, pos);
            if (result == null)
            {
                return false;
            }
            //查找元数据
            var sourcePerson = e.Data.GetData(e.Data.GetFormats()[0]);
            if (sourcePerson == null)
            {
                return false;
            }
            //查找目标数据
            var listBoxItem = ItemsControlDropSortExtendMethod.FindVisualParent<FrameworkElement>(result.VisualHit);
            if (listBoxItem == null)
            {
                return false;
            }
            from = sourcePerson;
            to = listBoxItem.DataContext;
            return true;
        }

        /// <summary>
        /// 移动列表元素 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemSource"></param>
        /// <param name="fromObj"></param>
        /// <param name="toObj"></param>
        /// <param name="pre"></param>
        public static void ChangeIetmIndex<T>(this Collection<T> itemSource, object fromObj, object toObj, int pre)
        {
            if (fromObj == null || toObj == null)
            {
                return;
            }

            if (!(fromObj is T) || !(toObj is T))
            {
                return;
            }

            if (fromObj.Equals(toObj))
            {
                return;
            }

            var fromItem = (T)fromObj;
            var toItem = (T)toObj;
            itemSource.Remove(fromItem);
            int indexTo = itemSource.IndexOf(toItem);
            if (indexTo == 0 && pre == 0)
            {
                itemSource.Insert(0, fromItem);
            }
            else if (indexTo == itemSource.Count - 1 && pre == 1)
            {
                itemSource.Add(fromItem);
            }
            else
            {
                itemSource.Insert(indexTo + pre, fromItem);
            }
        }

        /// <summary>
        /// 移动列表元素
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="itemSource"></param>
        /// <param name="itemObj"></param>
        /// <param name="pre"></param>
        public static void ChangeIetmIndex<T>(this Collection<T> itemSource, object itemObj, int pre)
        {
            if (itemObj == null)
            {
                return;
            }

            if (!(itemObj is T))
            {
                return;
            }

            var item = (T)itemObj;
            int indexItem = itemSource.IndexOf(item);
            if (indexItem == 0 && pre == -1)
            {
                //保持
            }
            else if (indexItem == itemSource.Count - 1 && pre == 1)
            {
                //保持
            }
            else
            {
                itemSource.Remove(item);
                itemSource.Insert(indexItem + pre, item);
            }
        }

    }

如何使用

界面部分

<Window x:Class="ItemsControlDragSortDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:ItemsControlDragSortDemo"
        xmlns:vm="clr-namespace:ItemsControlDragSortDemo"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainWindowViewModel  x:Name="MainWindowViewModel"/>
    </Window.DataContext>
    <Window.Resources>
        <Style x:Key="GridMouseOverHighLightStyle" TargetType="{x:Type local:DragGrid}">
            <Setter Property="Background" Value="#01FFFFFF" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#441AAA5E" />
                </Trigger>
                <Trigger Property="IsDragMouseOver" Value="True">
                    <Setter Property="Background" Value="#441AAA5E" />
                </Trigger>
            </Style.Triggers>
        </Style>

        <Style x:Key="GridMouseOverHighLightStyle2" TargetType="{x:Type local:DragGrid}">
            <Setter Property="Background" Value="#01FFFFFF" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Background" Value="#440000FF" />
                </Trigger>
                <Trigger Property="IsDragMouseOver" Value="True">
                    <Setter Property="Background" Value="#440000FF" />
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Grid>
        <ItemsControl x:Name="LBoxSort"
                      ItemsSource="{Binding YouItemSource}"
                      AllowDrop="True"
                      PreviewMouseMove="LBoxSort_OnPreviewMouseMove" 
                      >
            <!--Drop="LBoxSort_OnDrop"-->
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid Background="Red" Width="60" Height="30" Margin="10" >
                        <TextBlock Text="{Binding Name}"/>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition/>
                                <ColumnDefinition/>
                            </Grid.ColumnDefinitions>
                            <local:DragGrid Style="{StaticResource GridMouseOverHighLightStyle}"  Drop="LBoxSort_Drop_Prev" MouseLeftButtonDown="LBoxSort_Move_Prev" />
                            <local:DragGrid Style="{StaticResource GridMouseOverHighLightStyle2}" Drop="LBoxSort_Drop_Next" MouseLeftButtonDown="LBoxSort_Move_Next" Grid.Column="1"/>
                        </Grid>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</Window>

界面cs 辅助

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ItemsControlDragSortDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();
        }

        public MainWindowViewModel ViewModel { get => this.DataContext as MainWindowViewModel; }


        private void LBoxSort_OnPreviewMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                e.GetDragItem(sender, out object from);
                DragDrop.DoDragDrop(LBoxSort, from, DragDropEffects.Move);
            }
        }

        private void LBoxSort_Drop_Prev(object sender, DragEventArgs e)
        {
            var success = e.GetDropItem(sender, out object from, out object to);
            if (success)
            {
                ViewModel.ChangeIetmIndex(from, to, 0);
            }
        }

        private void LBoxSort_Drop_Next(object sender, DragEventArgs e)
        {
            var success = e.GetDropItem(sender, out object from, out object to);
            if (success)
            {
                ViewModel.ChangeIetmIndex(from, to, 1);
            }
        }

        private void LBoxSort_Move_Prev(object sender, MouseButtonEventArgs e)
        {
            var control = sender as FrameworkElement;
            var dc = control.DataContext;
            ViewModel.ChangeIetmIndex(dc, -1);
        }

        private void LBoxSort_Move_Next(object sender, MouseButtonEventArgs e)
        {
            var control = sender as FrameworkElement;
            var dc = control.DataContext;
            ViewModel.ChangeIetmIndex(dc, 1);
        }
    }
}

viewmodel 调用

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ItemsControlDragSortDemo
{
    public class MainWindowViewModel : ViewModelBase
    {

        public MainWindowViewModel()
        {

            List<YouItemViewModel> list = new List<YouItemViewModel>();
            for (int i = 0; i < 10; i++)
            {
                list.Add(new YouItemViewModel { Name = "" + i });
            }

            YouItemSource = new ObservableCollection<YouItemViewModel>(list);
        }

        private ObservableCollection<YouItemViewModel> _YouItemSource;

        public ObservableCollection<YouItemViewModel> YouItemSource { get => _YouItemSource; set => Set(ref _YouItemSource, value); }


        public class YouItemViewModel
        {
            public string Name { get; set; }
        }

        #region 拖拽移动顺序

        public void ChangeIetmIndex(object from, object to, int pre)
        {
            YouItemSource.ChangeIetmIndex(from, to, pre);
        }

        public void ChangeIetmIndex(object item, int pre)
        {
            YouItemSource.ChangeIetmIndex(item, pre);
        }

        #endregion
    }
}

viewmodel 基础类

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;

namespace ItemsControlDragSortDemo
{
    public class ViewModelBase : NotifyPropertyBase
    {

    }

    public class NotifyPropertyBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual bool Set<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.OnPropertyChanged(propertyName);

            return true;
        }

        protected void OnPropertyChanged(string propertyName)
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

另外拖拽的时候是没有mouseover效果的 所以 对Grid 做了一点调整 可以把这一段代码直接写在cs 也可以 重新搞个控件 我这里是重新搞控件继承了一下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace ItemsControlDragSortDemo
{
    public class DragGrid : Grid
    {

        public bool IsDragMouseOver
        {
            get { return (bool)GetValue(IsDragMouseOverProperty); }
            set { SetValue(IsDragMouseOverProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsDragMouseOver.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsDragMouseOverProperty =
            DependencyProperty.Register("IsDragMouseOver", typeof(bool), typeof(DragGrid), new PropertyMetadata(false));


        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);
            IsDragMouseOver = true;
        }

        protected override void OnDragLeave(DragEventArgs e)
        {
            base.OnDragLeave(e);
            IsDragMouseOver = false;
        }

        protected override void OnDragOver(DragEventArgs e)
        {
            base.OnDragOver(e);
            IsDragMouseOver = true;
        }

        protected override void OnDrop(DragEventArgs e)
        {
            base.OnDrop(e);
            IsDragMouseOver = false;
        }
    }
}

这样的话就能满足 拖拽每个元素 移动位置 和点击前后移动位置  要求低一点就点击前后移动位置就够了



版权声明:本文为sinat_30224769原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。