winform下重画ListBox
Windows Forms是由Win32 API封装的开发组件,最初是为了替代mfc,但却没有体现与Model View Controller架构对应的特色,进而在.net framework 3.0中推出了wpf,富控件数据显示方面,利用模板功能轻松实现。
在winform下要想自定义一些用户控件,就需要运用的2D绘画类。下图我们为ListBox重新排列了数据显示方式,并为每一个item加入了删除按钮。

首先我们设计一个承载数据的类ListBoxItem。
public class ListBoxItem : IDisposable
{
public Guid Id { get; set; } public string Name { get; set; } public string IP { get; set; } public string Mac { get; set; } [System.ComponentModel.DefaultValue(typeof(System.Drawing.Image), "null")]
public System.Drawing.Image Image { get; set; } public bool IsFocus { get; set; } public ListBoxItem() { } public ListBoxItem(Guid id, string name, string ip, string mac, System.Drawing.Image image)
{
this.Id = id;
this.Name = name;
this.IP = ip;
this.Mac = mac;
this.Image = image;
this.IsFocus = false;
} public void Dispose()
{
this.Image = null;
}
}
然后我们再为ListBox写一个用于展现数据的数据源ListBoxItemCollection,这里实现了迭代和集合操作接口,可以根据需要扩展数据操作方法。
[System.ComponentModel.ListBindable(false)]
public class ListBoxItemCollection : IList, ICollection, IEnumerable
{
private UserListBox m_owner; public ListBoxItemCollection(UserListBox owner)
{
this.m_owner = owner;
} internal UserListBox Owner
{
get { return this.m_owner; }
} #region override
public ListBoxItem this[int index]
{
get { return Owner.OldItemSource[index] as ListBoxItem; }
set { Owner.OldItemSource[index] = value; }
} public int Count
{
get { return Owner.OldItemSource.Count; }
} public bool IsReadOnly
{
get { return Owner.OldItemSource.IsReadOnly; }
} public int Add(ListBoxItem item)
{
if (item == null)
{
throw new ArgumentException("item is null");
}
return Owner.OldItemSource.Add(item);
} public void AddRange(ListBoxItem[] items)
{
Owner.OldItemSource.AddRange(items);
} public void Clear()
{
if (Owner.OldItemSource.Count > )
{
Owner.OldItemSource.Clear();
}
} public bool Contains(ListBoxItem item)
{
bool rst = false;
foreach (ListBoxItem oldItem in Owner.OldItemSource)
{
if (oldItem.Id == item.Id)
{
rst = true;
break;
}
}
return rst;
} public void CopyTo(ListBoxItem[] destination, int arrayIndex)
{
Owner.OldItemSource.CopyTo(destination, arrayIndex);
} public int IndexOf(ListBoxItem item)
{
return Owner.OldItemSource.IndexOf(item);
} public void Insert(int index, ListBoxItem item)
{
if (item == null)
{
throw new ArgumentException("item is null");
}
Owner.OldItemSource.Insert(index, item);
} public void Remove(ListBoxItem item)
{
Owner.OldItemSource.Remove(item);
} public void RemoveAt(int index)
{
Owner.OldItemSource.RemoveAt(index);
} public IEnumerator GetEnumerator()
{
return Owner.OldItemSource.GetEnumerator();
} int IList.Add(object value)
{
if (!(value is ListBoxItem))
{
throw new ArgumentException();
}
return Add(value as ListBoxItem);
} void IList.Clear()
{
Clear();
} bool IList.Contains(object value)
{
return Contains(value as ListBoxItem);
} int IList.IndexOf(object value)
{
return IndexOf(value as ListBoxItem);
} void IList.Insert(int index, object value)
{
if (!(value is ListBoxItem))
{
throw new ArgumentException();
}
Insert(index, value as ListBoxItem);
} bool IList.IsFixedSize
{
get { return false; }
} bool IList.IsReadOnly
{
get { return IsReadOnly; }
} void IList.Remove(object value)
{
Remove(value as ListBoxItem);
} void IList.RemoveAt(int index)
{
RemoveAt(index);
} object IList.this[int index]
{
get { return this[index]; }
set
{
if (!(value is ListBoxItem))
{
throw new ArgumentException();
}
this[index] = value as ListBoxItem;
}
} void ICollection.CopyTo(Array array, int index)
{
CopyTo((ListBoxItem[])array, index);
} int ICollection.Count
{
get { return Count; }
} bool ICollection.IsSynchronized
{
get { return false; }
} object ICollection.SyncRoot
{
get { return false; }
} IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion #region extention
public ListBoxItem FindByMac(string mac)
{
foreach (ListBoxItem item in Owner.OldItemSource)
{
if (item.Mac == mac)
{
return item;
}
}
return null;
}
#endregion
}
下面可以为工程new一个新项——自定义控件,命名为UserListBox。
这里有几个地方要说明一下,首先在默认构造函数里面的参数:
DrawMode.OwnerDrawVariable启用控件重绘功能。
DoubleBuffer开启后避免复杂绘画造成窗体闪烁,这个缓冲的原理是将绘画操作放在内存里操作,完成后才会复制到图形界面上,进而避免的闪烁。
OnPaint进行了重写,这个方法是根据pc屏幕分辨率刷新频率来执行的,会不断的重复执行,进而持久化图形界面。
Invalidate方法,会立即刷新UI。
Item上的按钮事件,是通过ListBox的click事件,取到鼠标的在界面上的定位,调用相对应的方法。
public partial class UserListBox : ListBox
{
public ListBoxItem mouseItem;
private ListBoxItemCollection m_Items; public UserListBox() : base()
{
InitializeComponent(); m_Items = new ListBoxItemCollection(this); base.DrawMode = DrawMode.OwnerDrawVariable;
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); // 双缓冲
this.SetStyle(ControlStyles.ResizeRedraw, true); // 调整大小时重绘
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景.
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); // 开启控件透明
} public new ListBoxItemCollection Items
{
get { return m_Items; }
} internal ListBox.ObjectCollection OldItemSource
{
get { return base.Items; }
} protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics; // you can set SeletedItem background
if (this.Focused && this.SelectedItem != null)
{
} for (int i = ; i < Items.Count; i++)
{
Rectangle bounds = this.GetItemRectangle(i); if (mouseItem == Items[i])
{
Color leftColor = Color.FromArgb(, , , );
using (SolidBrush brush = new SolidBrush(leftColor))
{
g.FillRectangle(brush, new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height));
} Color rightColor = Color.FromArgb(, , );
using (SolidBrush brush = new SolidBrush(rightColor))
{
g.FillRectangle(brush, new Rectangle(bounds.Width - , bounds.Y, , bounds.Height));
}
} int fontLeft = bounds.Left + + ;
System.Drawing.Font font = new System.Drawing.Font("微软雅黑", );
g.DrawString(Items[i].Name, font, new SolidBrush(this.ForeColor), fontLeft, bounds.Top + );
g.DrawString(Items[i].IP, font, new SolidBrush(Color.FromArgb(, , )), fontLeft, bounds.Top + );
g.DrawString(Items[i].Mac, font, new SolidBrush(Color.FromArgb(, , )), fontLeft, bounds.Top + ); if (Items[i].Image != null)
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
g.DrawImage(Items[i].Image, new Rectangle(bounds.X + , (bounds.Height - ) / + bounds.Top, , ));
}
g.DrawImage(Properties.Resources.error, new Rectangle(bounds.Width - , (bounds.Height - ) / + bounds.Top, , ));
}
base.OnPaint(e);
} protected override void OnMeasureItem(MeasureItemEventArgs e)
{
base.OnMeasureItem(e);
if (Items.Count > )
{
ListBoxItem item = Items[e.Index];
e.ItemHeight = ;
} } protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
} protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
for (int i = ; i < Items.Count; i++)
{
Rectangle bounds = this.GetItemRectangle(i);
Rectangle deleteBounds = new Rectangle(bounds.Width - , (bounds.Height - ) / + bounds.Top, , ); if (bounds.Contains(e.X, e.Y))
{
if (Items[i] != mouseItem)
{
mouseItem = Items[i];
} if (deleteBounds.Contains(e.X, e.Y))
{
mouseItem.IsFocus = true;
this.Cursor = Cursors.Hand;
}
else
{
mouseItem.IsFocus = false;
this.Cursor = Cursors.Arrow;
} this.Invalidate();
break;
}
}
} protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
if (mouseItem.IsFocus)
{
ListBoxItem deleteItem = mouseItem;
if(MessageBox.Show("confirm to delete", "", MessageBoxButtons.OKCancel) == DialogResult.OK)
{
this.Items.Remove(deleteItem);
}
}
} protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
this.mouseItem = null;
this.Invalidate();
}
}
winform下重画ListBox的更多相关文章
- Winform重画ComboBox背景色
//返回hWnd参数所指定的窗口的设备环境. [System.Runtime.InteropServices.DllImport("user32.dll")] static ext ...
- C# LIstbox 解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合”的问题
解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合”的问题 分类: winform2008-05-24 02:33 2592人阅读 评论(11) 收藏 举报 winf ...
- 解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合”
解决WinForm下ListBox控件“设置DataSource属性后无法修改项集合” 最近更新: 2013-2-15 587 很少写WinForm程序第一次使用ListBox控件就遇到了比 ...
- WinForm中重绘TabControl选项卡标题
最近开发WinForm频繁使用了TabControl控件,这个控件的选项卡没有BackgroundImage这个属性,那么如何为其各个选项卡添加背景图片呢?(这里说的是每个TabPage的头部,也就是 ...
- 分享在winform下实现左右布局多窗口界面
在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform.WPF中实现其实也很容易,我这里就分享一个:在winform下实现左右布局多窗口 ...
- WinForm下的loading框实现
前言:在项目使用C/S模式情况下,由于需要经常进行数据的刷新,如果直接进行刷新,会有一个等待控件重画的过程,非常的不友好,因此在这里添加一个loading框进行等待显示. 实现:在经过多方面查询资料, ...
- WinForm下的loading框的实现
前言:在项目使用C/S模式情况下,由于需要经常进行数据的刷新,如果直接进行刷新,会有一个等待控件重画的过程,非常的不友好,因此在这里添加一个loading框进行等待显示. 实现:在经过多方面查询资料, ...
- 在winform下实现左右布局多窗口界面的方法(一)
在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform.WPF中实现其实也很容易,通过本文给大家介绍在winform下实现左右布局多窗口 ...
- 重画GoogleClusterTrace数据
由于项目计划书写作需要,重画了Qi Zhang, Mohamed Faten Zhani, Raouf Boutaba, Joseph L. Hellerstein, Dynamic Heteroge ...
随机推荐
- nginx的特点
1.热部署 我个人觉得这个很不错.在master管理进程与worker工作进程的分离设计,使的Nginx具有热部署的功能,那么在7×24小时不间断服务的前提下,升级Nginx的可执行文件 ...
- 如何在asp.net中使用多线程及队列,异步处理一个耗时的任务(原创)
最近想在使用.net 的队列处理一些耗时的工作.经过考虑,需要先设计一个类,类中包含一个静态的队列.主要是写队列和读取队列. public class PaperCalculator { // 用于存 ...
- zabbix使用host metadata方式主动注册
host metadata是zabbix2.2新增加的功能,该功能在zabbix-agent端可以自定义条件,在选择自动注册的时候,zabbix-server端可以根据host metadata来选择 ...
- python 写文件,utf-8问题
写文件报数据. 同样的编码. 含中文字段的输出文件 编码为utf-8 无中文的却是asc import codecstxt = u”qwer”file=codecs.open(“test”,”w”,” ...
- python datatime
一.datetime 1.date date.today() 2.time 3.datetime datetime.now() datetime.strftime(fmt) 转换为字符串 dateti ...
- 工作中遇到的小问题: 做弹幕从数据库取出东西均匀插入marquee中,
function getFloatContent() { var method = 'GETFLOATCONTENT'; $.ajax({ url: 'api/zhenqiapi.php', data ...
- vnc 登录后只有终端 没有桌面 黑屏
1, start vnc server: vncserver :1 issue: connect it with pc and only display one terminal. 2, stop v ...
- 关于VMWARE 上对于不小心VMWare Tools Easy Install 后卡死的解决方法
PS 要想修改首先开机的时候会进入命令行,输入用户名,密码后,可以用startx来进入图形界面(也有人将/etc/init.d/ligtmd start可以懂事本人那次是不行的) 通常网上人们的解决办 ...
- CSS中父元素高度没有随子元素高度的改变而改变,应该如何解决?
如果子元素没有设置浮动(float),父元素实际上会根据内容,自动宽高进行适应的. 当子元素增加了浮动后,最简单的处理方法是给父元素添加overflow:hidden属性,此时父元素的高度会随子元素的 ...
- Java 第十章 类和对象
类和对象 类与对象的关系是什么? 答 :类是具有相同属性和方法的一组对象的集合. 类是抽象的,对象是具体的:类是对象的模版,对象是类的实例. 定义一个类的语法是什么? public class 类名{ ...