WPF4.0 DataGrid 拖拽行 排序
写在前边:内容,如题。事由,音乐播放器,歌曲排序。非原创,但有自主更新。
最终效果:
好吧,从头开始。公司要做一个简单的音乐播放器,其中音乐列表,肯定需要排序的。以前做过排序,很简单,如下图所示
虽完成功能,但操作体验不尽如人意。 因为其它播放器都是鼠标拖拽的。有 demo,所有功能在此demo基本之上
1,挖掘demo核心代码(已修正):
请首先确定 xaml中 DataGrid 添加以下属性: AllowDrop="True"
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public MainWindow() 2 { 3 InitializeComponent(); 4 this.Loaded += MainWindow_Loaded; 5 6 vm = new PublishViewModel(); 7 vm.Container = this; 8 this.DataContext = vm; 9 10 11 this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, OpenCmdExecuted)); 12 this.dg.PreviewMouseLeftButtonDown += dgEmployee_PreviewMouseLeftButtonDown; 13 this.dg.Drop += dgEmployee_Drop; 14 15 this.PreviewMouseUp += MainWindow_MouseUp; //需要完全监听鼠标弹起事件,未达到 16 17 this.dg.DragOver += dg_DragOver; 18 this.dg.DragLeave += dg_DragLeave; 19 this.dg.DragEnter += dg_DragEnter; 20 21 } 22 void MainWindow_MouseUp(object sender, MouseButtonEventArgs e) 23 { 24 LogHelper.Debug("mouseUp"); 25 } 26 27 void dg_DragEnter(object sender, DragEventArgs e) 28 { 29 if (e.Data.GetFormats()[0] == typeof(MusicInfo).ToString()) 30 { 31 32 } 33 else 34 { 35 MusicInfo m = new MusicInfo("添加到文件列表"); 36 m.IsNew = true; 37 this.vm.SelectedMusic = m; 38 } 39 40 } 41 42 void dg_DragLeave(object sender, DragEventArgs e) 43 { 44 this.popup1.IsOpen = false; 45 } 46 47 /// <summary> 48 /// 拖动时,对插入行进行着色处理。1,排序拖动;2,外部文件拖放 49 /// </summary> 50 /// <param name="sender"></param> 51 /// <param name="e"></param> 52 void dg_DragOver(object sender, DragEventArgs e) 53 { 54 55 int index = UITools.GetDataGridItemCurrentRowIndex(e.GetPosition, dg); 56 57 for (int i = 0; i < dg.Items.Count; i++) 58 { 59 DataGridRow r = dg.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow; 60 if (i != index) 61 { 62 r.BorderBrush = new SolidColorBrush(Colors.Transparent); 63 r.BorderThickness = new Thickness(0, 0, 0, 0); 64 } 65 else 66 { 67 r.BorderBrush = new SolidColorBrush(Color.FromRgb(32, 164, 230)); 68 Thickness th = new Thickness(0, 0, 0, 0); 69 70 if (index > prevRowIndex) th = new Thickness(0, 0, 0, 1); 71 else if (index < prevRowIndex) th = new Thickness(0, 1, 0, 0); 72 r.BorderThickness = th; 73 } 74 } 75 76 if (!popup1.IsOpen) 77 { 78 popup1.IsOpen = true; 79 } 80 Size popupSize = new Size(popup1.ActualWidth + 110, popup1.ActualHeight + 10); 81 Point p = e.GetPosition(this); 82 p.X += 10; 83 p.Y += 10; 84 popup1.PlacementRectangle = new Rect(p, popupSize); 85 86 // if (row != null) dg.SelectedItem = row.Item; 87 } 88 89 int prevRowIndex = -1; 90 /// <summary> 91 /// Defines the Drop Position based upon the index. 92 /// </summary> 93 /// <param name="sender"></param> 94 /// <param name="e"></param> 95 void dgEmployee_Drop(object sender, DragEventArgs e) 96 { 97 this.popup1.IsOpen = false; 98 int index = UITools.GetDataGridItemCurrentRowIndex(e.GetPosition, dg); 99 100 if (index < 0 || index == prevRowIndex) return; 101 (dg.ItemContainerGenerator.ContainerFromIndex(index) as DataGridRow).BorderThickness = new Thickness(0, 0, 0, 0); 102 103 104 if (index >= dg.Items.Count - 1) 105 { 106 index = dg.Items.Count - 1; 107 } 108 109 if (vm.SelectedMusic.IsNew) //表示拖动文件至表格 110 { 111 string[] obj = e.Data.GetData(DataFormats.FileDrop) as string[]; 112 foreach (string str in obj) 113 { 114 this.vm.CheckFile(str, ++index); 115 } 116 } 117 else 118 { 119 vm.FileList.RemoveAt(prevRowIndex); 120 vm.FileList.Insert(index, vm.SelectedMusic); 121 prevRowIndex = -1; 122 } 123 } 124 125 void dgEmployee_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 126 { 127 128 prevRowIndex = UITools.GetDataGridItemCurrentRowIndex(e.GetPosition, dg); 129 LogHelper.Debug("X " + e.GetPosition(this.dg).X.ToString()); 130 //如果选中区域在第一列,即点击选择框,则不进行 131 if (e.GetPosition(this.dg).X < 30) return; 132 133 if (prevRowIndex < 0) return; 134 dg.SelectedIndex = prevRowIndex; 135 136 MusicInfo selectedEmp = dg.Items[prevRowIndex] as MusicInfo; 137 138 if (selectedEmp == null) return; 139 140 DragDropEffects dragdropeffects = DragDropEffects.Move; 141 this.vm.SelectedMusic = selectedEmp; 142 if (DragDrop.DoDragDrop(dg, selectedEmp, dragdropeffects) 143 != DragDropEffects.None) 144 { 145 dg.SelectedItem = selectedEmp; 146 } 147 }
2,demo缺陷:
- 拖拽时没有鼠标跟随,不知道拖拽何物
- 准备放下时,不能显示准备插入的位置
- 启用拖指导致checkBox无法点击(估计是因为previewMouseDown事件,阻档了click事件消息)
3,针对以上缺陷的完善
- 鼠标跟随:xaml增 Popup块,隐藏。仅在拖动时显示。Popup展示内容与当选拖动行数据关联。
-
<Popup x:Name="popup1" IsHitTestVisible="False" Placement="RelativePoint" AllowsTransparency="True"> <Border BorderBrush="LightSteelBlue" BorderThickness="2" Background="White" Opacity="0.95"> <StackPanel Orientation="Horizontal" Margin="4,3,8,3"> <Image Source="res/icon.png" Width="16" Height="16" /> <TextBlock FontSize="14" FontWeight="Bold" VerticalAlignment="Center" Text="{Binding Path=SelectedMusic.Path}" Margin="8,0,0,0" /> </StackPanel> </Border> </Popup>
Popup要在 DragOver时显示。 见第一段代码: dg_DragOver方法中,位置的控制。
- 插入位置显示:dg_DragOver方法中,计算当前鼠标位置对应的数据行,更改数据行的样式,增加Border边框线显示,达到目的。 但需要循环所有表格行,不在鼠标对应行,则删除样式。估计比较占用cpu资源。
- 关于checkBox无法点击,发现问题后,试过在 MouseLeftButtonDown 进行拖拽动,这样checkBox可以点击但有50%的概念不能启动。后分析,计划依然使用 PreviewMouseLeftButtonDown。checkBox 在第一列,宽度40. 所以启动拖拽前,判断鼠标位置,x 是否 大于40。在小于40时,不启动,则checkBox完成点击事件。
4,其它:关于Popup的灵感,来自于另外一个demo,但没有链接,找不到了。它的大概意思是全完合用鼠标按下,移动,放下事件手写处理。而本例中使用的拖拽关键类:DragDrop.DoDragDrop()。再次感叹,条条道路通罗马。
5, 文字说明结束。文笔有限,附源码下载, 欢迎交流。
项目使用 vs2012编辑。不确定使用2010是否可以正常打开。