之前在写播放器的时候,遇到了一个问题,现在播放器无论是千千,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 == || e.Button != MouseButtons.Left || this.SelectedIndex == - || e.Clicks == )
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 < )
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 == || e.Button != MouseButtons.Left || this.SelectedIndex == - || e.Clicks == )
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 < ) return;
if (e.Index < ) return;
bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
if(selected)
e.Graphics.FillRectangle(selectRowBursh, e.Bounds);
else if (e.Index % != )
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的话,选择了一个元素,当在选择另一个元素的时候,之前选择过的元素的高亮状态不会消失,这个是什么原因我也没搞懂。如果有哪位园友知道的,麻烦指点一下。

  最后附上整个控件的源码

     public class DragableListBox:ListBox
{
#region 字段
private bool isDraw; //是否执行绘制
SolidBrush evenRowBursh ;
SolidBrush oddRowBursh;
Brush normalFontBursh=SystemBrushes.ControlText;
Brush selectFontBursh = SystemBrushes.HighlightText;
Brush selectRowBursh = SystemBrushes.Highlight;
private bool dragAcross;
private bool dragSort; public static ListBox dragSource;
#endregion public DragableListBox()
{
this.DoubleBuffered = true;
this.OddColor = this.BackColor;
} #region 外放成员 #region 属性 [Description("跨ListBox拖放元素"), Category("行为")]
public bool DragAcross
{
get { return (dragAcross&&AllowDrop&&SelectionMode== System.Windows.Forms.SelectionMode.One); }
set
{
dragAcross = value;
if (value) this.AllowDrop = true;
}
} [Description("元素拖动排序"),Category("行为")]
public bool DragSort
{
get { return dragSort && AllowDrop && SelectionMode == System.Windows.Forms.SelectionMode.One; }
set
{
dragSort = value;
if (value) this.AllowDrop = true;
}
} private Color oddColor;
[Description("单数行的底色"), Category("外观")]
public Color OddColor
{
get { return oddColor; }
set
{
oddColor = value;
isDraw = oddColor != this.BackColor;
if (isDraw) DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
else DrawMode = System.Windows.Forms.DrawMode.Normal;
}
} #endregion #region 事件 [Description("跨ListBox拖拽完成后触发"),Category("行为")]
public event DraggdHandler DraggedAcross; [Description("拖拽排序后触发"), Category("行为")]
public event DraggdHandler DraggedSort; #endregion #endregion #region 重写方法 #region 拖拽 protected override void OnDragDrop(DragEventArgs drgevent)
{
base.OnDragDrop(drgevent);
if (!DragAcross && !DragSort) return; object item = dragSource.SelectedItem; if (DragAcross && !DragSort && dragSource != this)
{
dragSource.Items.Remove(item);
this.Items.Add(item);
if (DraggedAcross != null)
DraggedAcross(this, new DraggedEventArgs() { DragItem=item, SourceControl=dragSource });
}
else if (DragSort &&(( dragSource == this&&! DragAcross)||DragAcross))
{
int index = this.IndexFromPoint(this.PointToClient(new Point(drgevent.X, drgevent.Y)));
dragSource.Items.Remove(item);
if (index < )
this.Items.Add(item);
else
this.Items.Insert(index, item);
if (DragAcross && DraggedAcross != null)
DraggedAcross(this, new DraggedEventArgs() { DragItem=item,SourceControl=dragSource });
if (DraggedSort != null)
DraggedSort(this, new DraggedEventArgs() { DragItem=item,SourceControl=dragSource, DestineIndex=index });
} } protected override void OnDragOver(DragEventArgs drgevent)
{
base.OnDragOver(drgevent);
if (!DragAcross&&!DragSort) return; //dragDestince=this;
drgevent.Effect = DragDropEffects.Move;
} protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
if (!DragAcross && !DragSort ) return; if (this.Items.Count == || e.Button != MouseButtons.Left || this.SelectedIndex == - || e.Clicks == )
return;
dragSource = this; int index = this.SelectedIndex;
object item = this.Items[index];
DragDropEffects dde = DoDragDrop(item,
DragDropEffects.All);
} #endregion #region 绘制 protected override void OnDrawItem(DrawItemEventArgs e)
{
if (this.Items.Count < ) return;
if (!isDraw) return;
if (e.Index < ) return;
bool selected = (e.State & DrawItemState.Selected) == DrawItemState.Selected;
if(selected)
//if (e.Index == this.SelectedIndex)
e.Graphics.FillRectangle(selectRowBursh, e.Bounds);
else if (e.Index % != )
e.Graphics.FillRectangle(OddRowBursh, e.Bounds);
else
e.Graphics.FillRectangle(EvenRowBursh, e.Bounds); //if (e.Index == this.SelectedIndex )
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);
} protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (oddRowBursh != null) oddRowBursh.Dispose();
if (evenRowBursh != null) evenRowBursh.Dispose();
} #endregion #endregion #region 私有方法和属性 private SolidBrush EvenRowBursh
{
get
{
if(evenRowBursh==null)
{
evenRowBursh = new SolidBrush(this.BackColor);
return evenRowBursh;
}
if ( evenRowBursh.Color == this.BackColor)return evenRowBursh;
evenRowBursh.Dispose();
evenRowBursh = new SolidBrush(this.BackColor);
return evenRowBursh;
}
set
{
if(evenRowBursh!=null) evenRowBursh.Dispose();
evenRowBursh = value;
}
} private SolidBrush OddRowBursh
{
get
{
if (oddRowBursh == null)
{
oddRowBursh = new SolidBrush(this.OddColor);
return oddRowBursh;
}
if (oddRowBursh.Color == this.OddColor) return oddRowBursh;
oddRowBursh.Dispose();
oddRowBursh = new SolidBrush(this.OddColor);
return oddRowBursh;
}
set
{
if (oddRowBursh != null) oddRowBursh.Dispose();
oddRowBursh = value;
}
} private string GetItemText(int index)
{
try
{
object item = this.Items[index];
if (string.IsNullOrEmpty(this.DisplayMember) || string.IsNullOrWhiteSpace(this.DisplayMember))
return item.ToString();
PropertyInfo proInfo = item.GetType().GetProperty(this.DisplayMember);
return proInfo.GetValue(item, null).ToString();
}
catch { return this.Name; }
} #endregion public class DraggedEventArgs:EventArgs
{
public ListBox SourceControl { get; set; } public object DragItem { get; set; } public int DestineIndex { get; set; } public DraggedEventArgs()
{
DestineIndex = -;
}
} public delegate void DraggdHandler(object sender, DraggedEventArgs e);
}

DragableListBox

可拖拽的ListBox的更多相关文章

  1. 【WPF】拖拽ListBox中的Item

    整理了两个关于WPF拖拽ListBox中的Item的功能.项目地址 https://github.com/Guxin233/WPF-DragItemInListBox 需求一: 两个ListBox,拖 ...

  2. WPF MultiSelect模式下ListBox 实现多个ListBoxItem拖拽

    WPF 的ListBox不支持很多常见的用户习惯,如在Explorer中用鼠标可以选择多项Item,并且点击已经选择的Item,按住鼠标左键可以将所有已选择Item拖拽到指定的位置.本文简单的实现了这 ...

  3. ListBox实现拖拽排序功能

    1.拖拽需要实现的事件包括: PreviewMouseLeftButtonDown LBoxSort_OnDrop 具体实现如下: private void LBoxSort_OnPreviewMou ...

  4. MVVM模式下实现拖拽

    在文章开始之前先看一看效果图 我们可以拖拽一个"游戏"给ListBox,并且ListBox也能接受拖拽过来的数据, 但是我们不能拖拽一个"游戏类型"给它. 所以 ...

  5. WinForm实现简单的拖拽功能(C#)

    用到了ListBox和TreeView两个控件,ListBox作为数据源,通过拖拽其中的数据放置到TreeView上,自动添加一个树节点 ListBox控件的MouseDown用于获取要拖拽的值并调用 ...

  6. WPF 实现控件间拖拽内容

    想实现这样一个常用功能:在ListBox的一个Item上点住左键,然后拖拽到另外一个控件(如ListView中),松开左键,数据已经拖拽过来. 步骤如下: 1. 设置ListBox 的AllowDro ...

  7. WPF中元素拖拽的两个实例

    今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListV ...

  8. 【C#/WPF】GridSplitter 分割布局,拖拽控件分隔栏以改变控件尺寸

    需求:界面由多部分控件组成,想要拖拽控件之间的分隔栏以改变尺寸. MainWindow.xaml: <Grid> <Grid.ColumnDefinitions> <Co ...

  9. wpf拖拽

    简单拖拽的实现是,实现源控件的MouseDown事件,和目标控件Drop事件.调用DragDrop.DoDragDrop()以启动拖放操作,DragDrop.DoDragDrop()函数接受三个参数: ...

随机推荐

  1. Oracle中Kill session的研究(转 出自eagle)

    itpub link: http://www.itpub.net/235873.html 我们知道,在Oracle数据库中,可以通过kill session的方式来终止一个进程,其基本语法结构为: a ...

  2. C#打开摄像头抓取照片然后退出

    using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; namespa ...

  3. ECSHOP后台SQL查询提示错误 this sql May contain UPDATE,DELETE,TRUNCATE,ALTER,DROP,FLUSH,INSERT

    一).首先说一下错误现象:市面上流行的绝大部分ECSHOP模板,安装的时候都需要执行一段或几段SQL语句来修改数据结构或者初始化一些数据.大多数ECSHOP管理员为了省事,都会通过 “ECSHOP后台 ...

  4. 卖萌的极致!脸部捕捉软件FaceRig让你化身萌宠

    FaceRig是一款以摄像头为跟踪设备捕捉用户脸部动作并转化为数据套用在其他动画模型上的一款软件,能够应用于一些日常的视频社交软件或网站,比如视频通话软件Skype和直播网站Twitch.FaceRi ...

  5. IOS 开发环境,证书和授权文件等详解

    (转自:http://blog.csdn.net/gtncwy/article/details/8617788) 一.成员介绍1.    Certification(证书)证书是对电脑开发资格的认证, ...

  6. 分布式并行数据库将在OLTP 领域促进去“Oracle”

    原文链接:http://www.csdn.net/article/2015-09-11/2825678 摘要:本文全面介绍了分布式数据库和它的设计理念,以及分布式数据库的优势和应用场景,从而引出OLT ...

  7. TabHost的用法(转)

    本文结合源代码和实例来说明TabHost的用法. 使用TabHost 可以在一个屏幕间进行不同版面的切换,例如android自带的拨号应用,截图:  查看tabhost的源代码,主要实例变量有: pr ...

  8. ubuntu 修改默认root及密码

    安装完Ubuntu后忽然意识到没有设 置root密码,不知道密码自然就无法进入根用户下.到网上搜了一下,原来是这麽回事.Ubuntu的默认root密码是随机的,即每次开机都有一个新的 root密码.我 ...

  9. Git 执行 「fork 出来的仓库」和「最新版本的原仓库」内容同步更新

    当我们在 GitHub 上 fork 出一个仓库后,如果原仓库更新了,此时怎样才能保证我们 fork 出来的仓库和原仓库内容一致呢?我们一般关注的是仓库的 master(主干分支)的内容,通过以下步骤 ...

  10. 明天去FDUSC报道了,GOD BLESS ALL OF US

    @lrb @tellmewtf @proverbs