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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM