Windows Forms是由Win32 API封裝的開發組件,最初是爲了替代mfc,但卻沒有體現與Model View Controller架構對應的特點,進而在.net framework 3.0中推出了wpf,富控件數據顯示方面,利用模板功能輕鬆實現。架構
在winform下要想自定義一些用戶控件,就須要運用的2D繪畫類。下圖咱們爲ListBox從新排列了數據顯示方式,併爲每個item加入了刪除按鈕。ide
首先咱們設計一個承載數據的類ListBoxItem。函數
1 public class ListBoxItem : IDisposable 2 { 3 public Guid Id { get; set; } 4 5 public string Name { get; set; } 6 7 public string IP { get; set; } 8 9 public string Mac { get; set; } 10 11 [System.ComponentModel.DefaultValue(typeof(System.Drawing.Image), "null")] 12 public System.Drawing.Image Image { get; set; } 13 14 public bool IsFocus { get; set; } 15 16 public ListBoxItem() { } 17 18 public ListBoxItem(Guid id, string name, string ip, string mac, System.Drawing.Image image) 19 { 20 this.Id = id; 21 this.Name = name; 22 this.IP = ip; 23 this.Mac = mac; 24 this.Image = image; 25 this.IsFocus = false; 26 } 27 28 public void Dispose() 29 { 30 this.Image = null; 31 } 32 }
而後咱們再爲ListBox寫一個用於展示數據的數據源ListBoxItemCollection,這裏實現了迭代和集合操做接口,能夠根據須要擴展數據操做方法。ui
[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 > 0) { 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。this
這裏有幾個地方要說明一下,首先在默認構造函數裏面的參數:spa
DrawMode.OwnerDrawVariable啓用控件重繪功能。.net
DoubleBuffer開啓後避免複雜繪畫形成窗體閃爍,這個緩衝的原理是將繪畫操做放在內存裏操做,完成後纔會複製到圖形界面上,進而避免的閃爍。設計
OnPaint進行了重寫,這個方法是根據pc屏幕分辨率刷新頻率來執行的,會不斷的重複執行,進而持久化圖形界面。code
Invalidate方法,會當即刷新UI。orm
Item上的按鈕事件,是經過ListBox的click事件,取到鼠標的在界面上的定位,調用相對應的方法。
1 public partial class UserListBox : ListBox 2 { 3 public ListBoxItem mouseItem; 4 private ListBoxItemCollection m_Items; 5 6 public UserListBox() : base() 7 { 8 InitializeComponent(); 9 10 m_Items = new ListBoxItemCollection(this); 11 12 base.DrawMode = DrawMode.OwnerDrawVariable; 13 this.SetStyle(ControlStyles.UserPaint, true); 14 this.SetStyle(ControlStyles.DoubleBuffer, true); // 雙緩衝 15 this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); // 雙緩衝 16 this.SetStyle(ControlStyles.ResizeRedraw, true); // 調整大小時重繪 17 this.SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. 18 this.SetStyle(ControlStyles.SupportsTransparentBackColor, true); // 開啓控件透明 19 } 20 21 public new ListBoxItemCollection Items 22 { 23 get { return m_Items; } 24 } 25 26 internal ListBox.ObjectCollection OldItemSource 27 { 28 get { return base.Items; } 29 } 30 31 protected override void OnPaint(PaintEventArgs e) 32 { 33 Graphics g = e.Graphics; 34 35 // you can set SeletedItem background 36 if (this.Focused && this.SelectedItem != null) 37 { 38 } 39 40 for (int i = 0; i < Items.Count; i++) 41 { 42 Rectangle bounds = this.GetItemRectangle(i); 43 44 if (mouseItem == Items[i]) 45 { 46 Color leftColor = Color.FromArgb(200, 192, 224, 248); 47 using (SolidBrush brush = new SolidBrush(leftColor)) 48 { 49 g.FillRectangle(brush, new Rectangle(bounds.X, bounds.Y, bounds.Width, bounds.Height)); 50 } 51 52 Color rightColor = Color.FromArgb(252, 233, 161); 53 using (SolidBrush brush = new SolidBrush(rightColor)) 54 { 55 g.FillRectangle(brush, new Rectangle(bounds.Width - 40, bounds.Y, 40, bounds.Height)); 56 } 57 } 58 59 int fontLeft = bounds.Left + 40 + 15; 60 System.Drawing.Font font = new System.Drawing.Font("微軟雅黑", 9); 61 g.DrawString(Items[i].Name, font, new SolidBrush(this.ForeColor), fontLeft, bounds.Top + 5); 62 g.DrawString(Items[i].IP, font, new SolidBrush(Color.FromArgb(128, 128, 128)), fontLeft, bounds.Top + 20); 63 g.DrawString(Items[i].Mac, font, new SolidBrush(Color.FromArgb(128, 128, 128)), fontLeft, bounds.Top + 35); 64 65 if (Items[i].Image != null) 66 { 67 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; 68 g.DrawImage(Items[i].Image, new Rectangle(bounds.X + 5, (bounds.Height - 40) / 2 + bounds.Top, 40, 40)); 69 } 70 g.DrawImage(Properties.Resources.error, new Rectangle(bounds.Width - 28, (bounds.Height - 16) / 2 + bounds.Top, 16, 16)); 71 } 72 base.OnPaint(e); 73 } 74 75 protected override void OnMeasureItem(MeasureItemEventArgs e) 76 { 77 base.OnMeasureItem(e); 78 if (Items.Count > 0) 79 { 80 ListBoxItem item = Items[e.Index]; 81 e.ItemHeight = 54; 82 } 83 84 } 85 86 protected override void OnSelectedIndexChanged(EventArgs e) 87 { 88 base.OnSelectedIndexChanged(e); 89 } 90 91 protected override void OnMouseMove(MouseEventArgs e) 92 { 93 base.OnMouseMove(e); 94 for (int i = 0; i < Items.Count; i++) 95 { 96 Rectangle bounds = this.GetItemRectangle(i); 97 Rectangle deleteBounds = new Rectangle(bounds.Width - 28, (bounds.Height - 16) / 2 + bounds.Top, 16, 16); 98 99 if (bounds.Contains(e.X, e.Y)) 100 { 101 if (Items[i] != mouseItem) 102 { 103 mouseItem = Items[i]; 104 } 105 106 if (deleteBounds.Contains(e.X, e.Y)) 107 { 108 mouseItem.IsFocus = true; 109 this.Cursor = Cursors.Hand; 110 } 111 else 112 { 113 mouseItem.IsFocus = false; 114 this.Cursor = Cursors.Arrow; 115 } 116 117 this.Invalidate(); 118 break; 119 } 120 } 121 } 122 123 protected override void OnMouseClick(MouseEventArgs e) 124 { 125 base.OnMouseClick(e); 126 if (mouseItem.IsFocus) 127 { 128 ListBoxItem deleteItem = mouseItem; 129 if(MessageBox.Show("confirm to delete", "", MessageBoxButtons.OKCancel) == DialogResult.OK) 130 { 131 this.Items.Remove(deleteItem); 132 } 133 } 134 } 135 136 protected override void OnMouseLeave(EventArgs e) 137 { 138 base.OnMouseLeave(e); 139 this.mouseItem = null; 140 this.Invalidate(); 141 } 142 }