可拖拽的ListBox

  之前在写播放器的时候,遇到了一个问题,现在播放器无论是千千,KuGoo还是比较原始的MediaPlayer,它们的播放表都是可以拖拽的,直接把文件拖到播放表实现歌曲的添加那个先暂且不说,光是播放表里面的歌曲次序也可以通过拖拽来调整。但是VS提供的ListBox没能直接通过设定某个属性实现这个拖拽排序,于是俺就开始了实现这功能的探索,无意中还找到了ListBox与ListBox之间元素的拖拽,于是一并实现了,遂述此文以记之。

  其实无论是ListBox里的拖拽排序,还是ListBox间的拖动,都是通过三个事件来实现的:DragDrop,DragOver和MouseDown,对于整个拖拽的过程来说,三个事件的触发顺序是MouseDown->DragOver->DragDrop。对于拖拽排序和控件间的拖动,代码会有所差异。下面则一分为二的说说各自的处理,由于我这里是扩展控件的,直接重写ListBox类的Onxxx方法。如果直接使用ListBox控件的话,就要在这三个事件绑定的方法里面写了。

  拖拽排序  

        protected override void  OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            if (this.Items.Count == 0 || e.Button != MouseButtons.Left || this.SelectedIndex == -1 || e.Clicks == 2)
                return;
            
            int index = this.SelectedIndex;
            object item = this.Items[index];
            DragDropEffects dde = DoDragDrop(item,
                DragDropEffects.All);
        }

首先要判断一下当前的ListBox有没有元素,还有鼠标有没有点中元素。接着到拖拽的过程

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            base.OnDragOver(drgevent);

            drgevent.Effect = DragDropEffects.Move;
        }

最后到拖放结束,鼠标按键松开的时候触发

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            base.OnDragDrop(drgevent);

            object item = dragSource.SelectedItem;

                int index = this.IndexFromPoint(this.PointToClient(new Point(drgevent.X, drgevent.Y)));
                this.Items.Remove(item);
                if (index < 0)
                    this.Items.Add(item);
                else
                    this.Items.Insert(index, item);
        }

主要是获取那个被选中(后来就被拖动)的那个元素,然后把它从ListBox先移除,从鼠标当前的坐标来获取到所在元素的索引值,那个位置,那个索引就是被拖拽的元素的新位置,用Insert把它插进去。

  ListBox间的拖动

 

  这个就要先知道各个方法究竟是那个控件的事件触发时调用的。其余大体上跟上面的是一致的。

  举个例子,现在有两个ListBox lst1,lst2。我把lst1的一个元素拖到lst2中去,事件触发的队列是这样的

  Lst1.MouseDown=>lst1.DragOver=>lst1.DragOver=>….lst1.DragOver=>lst2.DragOver=>lst2.DragOver=>…..=>lst2.DragOver=>lst2.DragDrop

由于整个流程涉及到两个控件,最后元素的添加与元素的删除分别是两个控件的事,于是我这里就另外声明多一个静态字段来存放那个lst1。记录来源的ListBox只能在MouseDown记录了。

public static ListBox dragSource;

        protected override void  OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);

            if (this.Items.Count == 0 || e.Button != MouseButtons.Left || this.SelectedIndex == -1 || e.Clicks == 2)
                return;
            dragSource = this;
            
            int index = this.SelectedIndex;
            object item = this.Items[index];
            DragDropEffects dde = DoDragDrop(item,
                DragDropEffects.All);
        }

除了dragSource = this;外,其余都与拖拽排序的一样。

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            base.OnDragOver(drgevent);
            drgevent.Effect = DragDropEffects.Move;
        }

这个可以说跟拖拽排序的一模一样了。

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            base.OnDragDrop(drgevent);

            object item = dragSource.SelectedItem;

            if ( dragSource != this)
            {
                dragSource.Items.Remove(item);
                this.Items.Add(item);
            }
            
        }

  因为是ListBox间的拖动,所以源ListBox和目标ListBox不能一样,处理还更简单,从源ListBox把元素删掉,然后增加到当前的ListBox中来。 

  如果想要既要控件间的拖动,拖动后又要按位置插入,那就把两个处理融合一下咯,本文末尾有我拓展控件的整份源码。需要的园友可以展开来看一下。

  

  交替颜色

 

  下面还介绍我另外一个拓展,就是单双行的交替颜色。记得以前的千千的播放表是有交替颜色的,现在的不知道,太久没用了,KuGoo现在的没有了,MediaPlayer12的也没有。

  我这里是重写OnDrawItem,直接拖控件的可以用DrawItem时间,接下来就是GDI+的内容了。

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if (this.Items.Count < 0) return;
            if (e.Index < 0) return;
            bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
            if(selected)
                e.Graphics.FillRectangle(selectRowBursh, e.Bounds);
            else if (e.Index % 2 != 0)
                e.Graphics.FillRectangle(OddRowBursh, e.Bounds);
            else
                e.Graphics.FillRectangle(EvenRowBursh, e.Bounds);

            if(selected)
            {
                e.Graphics.DrawString(this.GetItemText(e.Index), e.Font,
                            selectFontBursh, e.Bounds);
                
            }
            else
            {
                e.Graphics.DrawString(this.GetItemText(e.Index), e.Font,
                            normalFontBursh, e.Bounds);
            }
            e.DrawFocusRectangle();
            base.OnDrawItem(e);
        }

  值得一提的是这里判断选中的不是用e.Index==this.SelectedIndex,而是用(e.State & DrawItemState.Selected) == DrawItemState.Selected,当ListBox的选择模式用了多选而不是单选的时候,调用Select属性会报错,这个也是上面拖拽的局限性,当ListBox是多选的时候,上面的拖拽就会抛异常了,而且如果单纯用e.Index==this.SelectIndex的话,选择了一个元素,当在选择另一个元素的时候,之前选择过的元素的高亮状态不会消失,这个是什么原因我也没搞懂。如果有哪位园友知道的,麻烦指点一下。

  最后附上整个控件的源码

 

  1     public class DragableListBox:ListBox
  2     {
  3         #region 字段
  4         private bool isDraw; //是否执行绘制
  5         SolidBrush evenRowBursh ;
  6         SolidBrush oddRowBursh;
  7         Brush normalFontBursh=SystemBrushes.ControlText;
  8         Brush selectFontBursh = SystemBrushes.HighlightText;
  9         Brush selectRowBursh =  SystemBrushes.Highlight;
 10         private bool dragAcross;
 11         private bool dragSort;
 12 
 13         public static ListBox dragSource;
 14         #endregion
 15 
 16         public DragableListBox()
 17         {
 18             this.DoubleBuffered = true;
 19             this.OddColor = this.BackColor;
 20         }
 21 
 22         #region 外放成员
 23 
 24         #region 属性
 25 
 26         [Description("跨ListBox拖放元素"), Category("行为")]
 27         public bool DragAcross 
 28         {
 29             get { return (dragAcross&&AllowDrop&&SelectionMode== System.Windows.Forms.SelectionMode.One); }
 30             set 
 31             {
 32                 dragAcross = value;
 33                 if (value) this.AllowDrop = true;
 34             }
 35         }
 36 
 37         [Description("元素拖动排序"),Category("行为")]
 38         public bool DragSort
 39         {
 40             get { return dragSort && AllowDrop && SelectionMode == System.Windows.Forms.SelectionMode.One; }
 41             set
 42             {
 43                 dragSort = value;
 44                 if (value) this.AllowDrop = true;
 45             }
 46         }
 47 
 48         private Color oddColor;
 49         [Description("单数行的底色"), Category("外观")]
 50         public Color OddColor
 51         {
 52             get { return oddColor; }
 53             set 
 54             {
 55                 oddColor = value;
 56                 isDraw = oddColor != this.BackColor;
 57                 if (isDraw) DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
 58                 else DrawMode = System.Windows.Forms.DrawMode.Normal;
 59             }
 60         }
 61 
 62         #endregion
 63 
 64         #region 事件
 65 
 66         [Description("跨ListBox拖拽完成后触发"),Category("行为")]
 67         public event DraggdHandler DraggedAcross;
 68 
 69         [Description("拖拽排序后触发"), Category("行为")]
 70         public event DraggdHandler DraggedSort;
 71 
 72         #endregion
 73 
 74         #endregion
 75 
 76         #region 重写方法
 77 
 78         #region 拖拽
 79 
 80         protected override void OnDragDrop(DragEventArgs drgevent)
 81         {
 82             base.OnDragDrop(drgevent);
 83             if (!DragAcross && !DragSort) return;
 84 
 85             object item = dragSource.SelectedItem;
 86 
 87             if (DragAcross && !DragSort && dragSource != this)
 88             {
 89                 dragSource.Items.Remove(item);
 90                 this.Items.Add(item);
 91                 if (DraggedAcross != null)
 92                     DraggedAcross(this, new DraggedEventArgs() { DragItem=item, SourceControl=dragSource });
 93             }
 94             else if (DragSort &&(( dragSource == this&&! DragAcross)||DragAcross))
 95             {
 96                 int index = this.IndexFromPoint(this.PointToClient(new Point(drgevent.X, drgevent.Y)));
 97                 dragSource.Items.Remove(item);
 98                 if (index < 0)
 99                     this.Items.Add(item);
100                 else
101                     this.Items.Insert(index, item);
102                 if (DragAcross && DraggedAcross != null)
103                     DraggedAcross(this, new DraggedEventArgs() { DragItem=item,SourceControl=dragSource });
104                 if (DraggedSort != null)
105                     DraggedSort(this, new DraggedEventArgs() { DragItem=item,SourceControl=dragSource, DestineIndex=index });
106             }
107             
108         }
109 
110         protected override void OnDragOver(DragEventArgs drgevent)
111         {
112             base.OnDragOver(drgevent);
113             if (!DragAcross&&!DragSort) return;
114 
115             //dragDestince=this;
116             drgevent.Effect = DragDropEffects.Move;
117         }
118 
119         protected override void  OnMouseDown(MouseEventArgs e)
120         {
121             base.OnMouseDoubleClick(e);
122             if (!DragAcross && !DragSort ) return;
123 
124             if (this.Items.Count == 0 || e.Button != MouseButtons.Left || this.SelectedIndex == -1 || e.Clicks == 2)
125                 return;
126             dragSource = this;
127             
128             int index = this.SelectedIndex;
129             object item = this.Items[index];
130             DragDropEffects dde = DoDragDrop(item,
131                 DragDropEffects.All);
132         }
133 
134         #endregion 
135 
136         #region 绘制
137 
138         protected override void OnDrawItem(DrawItemEventArgs e)
139         {
140             if (this.Items.Count < 0) return;
141             if (!isDraw) return;
142             if (e.Index < 0) return;
143             bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
144             if(selected)
145             //if (e.Index == this.SelectedIndex)
146                 e.Graphics.FillRectangle(selectRowBursh, e.Bounds);
147             else if (e.Index % 2 != 0)
148                 e.Graphics.FillRectangle(OddRowBursh, e.Bounds);
149             else
150                 e.Graphics.FillRectangle(EvenRowBursh, e.Bounds);
151 
152             //if (e.Index == this.SelectedIndex )
153             if(selected)
154             {
155                 e.Graphics.DrawString(this.GetItemText(e.Index), e.Font,
156                             selectFontBursh, e.Bounds);
157                 
158             }
159             else
160             {
161                 e.Graphics.DrawString(this.GetItemText(e.Index), e.Font,
162                             normalFontBursh, e.Bounds);
163             }
164             e.DrawFocusRectangle();
165             base.OnDrawItem(e);
166         }
167 
168         protected override void Dispose(bool disposing)
169         {
170             base.Dispose(disposing);
171             if (oddRowBursh != null) oddRowBursh.Dispose();
172             if (evenRowBursh != null) evenRowBursh.Dispose();
173         }
174 
175         #endregion
176 
177         #endregion
178 
179         #region 私有方法和属性
180 
181         private SolidBrush EvenRowBursh
182         {
183             get 
184             {
185                 if(evenRowBursh==null)
186                 {
187                     evenRowBursh = new SolidBrush(this.BackColor);
188                     return evenRowBursh;
189                 }
190                 if ( evenRowBursh.Color == this.BackColor)return evenRowBursh;
191                 evenRowBursh.Dispose();
192                 evenRowBursh = new SolidBrush(this.BackColor);
193                 return evenRowBursh;
194             }
195             set 
196             {
197                 if(evenRowBursh!=null) evenRowBursh.Dispose();
198                 evenRowBursh = value;
199             }
200         }
201 
202         private SolidBrush OddRowBursh
203         {
204             get 
205             {
206                 if (oddRowBursh == null)
207                 {
208                     oddRowBursh = new SolidBrush(this.OddColor);
209                     return oddRowBursh;
210                 }
211                 if (oddRowBursh.Color == this.OddColor) return oddRowBursh;
212                 oddRowBursh.Dispose();
213                 oddRowBursh = new SolidBrush(this.OddColor);
214                 return oddRowBursh;
215             }
216             set 
217             {
218                 if (oddRowBursh != null) oddRowBursh.Dispose();
219                 oddRowBursh = value;
220             }
221         }
222 
223         private string GetItemText(int index)
224         {
225             try
226             {
227                 object item = this.Items[index];
228                 if (string.IsNullOrEmpty(this.DisplayMember) || string.IsNullOrWhiteSpace(this.DisplayMember))
229                     return item.ToString();
230                 PropertyInfo proInfo = item.GetType().GetProperty(this.DisplayMember);
231                 return proInfo.GetValue(item, null).ToString();
232             }
233             catch { return this.Name; }
234         }
235 
236         #endregion
237 
238         public class DraggedEventArgs:EventArgs
239         {
240             public ListBox SourceControl { get; set; }
241 
242             public object DragItem { get; set; }
243 
244             public int DestineIndex { get; set; }
245 
246             public DraggedEventArgs()
247             {
248                 DestineIndex = -1;
249             }
250         }
251 
252         public delegate void DraggdHandler(object sender, DraggedEventArgs e);
253     }
DragableListBox

 

posted @ 2013-09-04 08:13  猴健居士  阅读(4151)  评论(5编辑  收藏  举报