可拖拽的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的话,选择了一个元素,当在选择另一个元素的时候,之前选择过的元素的高亮状态不会消失,这个是什么原因我也没搞懂。如果有哪位园友知道的,麻烦指点一下。

  最后附上整个控件的源码

 

可拖拽的ListBox
  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

 

你可能感兴趣的:(listbox)