我們可以自定義DataGridView的DataGridViewColumn來實現自定義的列,下面介紹一下如何通過擴展DataGridViewColumn來實現一個TreeViewColumn
TreeViewColumn繼承自DataGridViewColumn,為了動態給TreeViewColumn傳入一個TreeView,這裡暴露出一個公共屬性_root,可以綁定一個初始化的TreeView. 另外需要重寫DataGridCell類型的CellTemplate,這裡返還一個TreeViewCell(需要自定義)
1 /// <summary> 2 /// Host TreeView In DataGridView Cell 3 /// </summary> 4 public class TreeViewColumn : DataGridViewColumn 5 { 6 public TreeViewColumn() 7 : base(new TreeViewCell()) 8 { 9 } 10 [Description("Set TreeView Root in DataGridView Cell"), Category("TreeView")] 11 public TreeView _root 12 { 13 get{return Roots.tree;} 14 set{Roots.tree=value;} 15 } 16 public override DataGridViewCell CellTemplate 17 { 18 get 19 { 20 return base.CellTemplate; 21 } 22 set 23 { 24 // Ensure that the cell used for the template is a TreeViewCell. 25 if (value != null && 26 !value.GetType().IsAssignableFrom(typeof(TreeViewCell))) 27 { 28 throw new InvalidCastException("Must be a TreeViewCell"); 29 } 30 base.CellTemplate = value; 31 } 32 } 33 }
上面TreeViewColumn重寫了CellTemplate,返回的就是自定義的TreeViewCell,這裡就是具體實現其邏輯。一般來說選擇樹控件的節點後,返回的是一個文本信息,是文本類型,可以繼承DataGridViewTextBoxCell,並重寫InitializeEditingControl來進行自定義的DataGridView.EditingControl (編輯控件)。
1 public class TreeViewCell : DataGridViewTextBoxCell 2 { 3 4 public TreeViewCell() 5 : base() 6 { 7 8 //初始設置 9 } 10 11 public override void InitializeEditingControl(int rowIndex, object 12 initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) 13 { 14 // Set the value of the editing control to the current cell value. 15 base.InitializeEditingControl(rowIndex, initialFormattedValue, 16 dataGridViewCellStyle); 17 TreeViewEditingControl ctl = 18 DataGridView.EditingControl as TreeViewEditingControl; 19 // Use the default row value when Value property is null. 20 if (this.Value == null) 21 { 22 23 ctl.SelectedNode =new TreeNode( this.DefaultNewRowValue.ToString()); 24 } 25 else 26 { 27 ctl.SelectedNode = new TreeNode(this.Value.ToString()); 28 } 29 } 30 31 public override Type EditType 32 { 33 get 34 { 35 // Return the type of the editing control that CalendarCell uses. 36 return typeof(TreeViewEditingControl); 37 } 38 } 39 40 public override Type ValueType 41 { 42 get 43 { 44 // Return the type of the value that CalendarCell contains. 45 return typeof(String); 46 } 47 } 48 49 public override object DefaultNewRowValue 50 { 51 get 52 { 53 // Use the current date and time as the default value. 54 return ""; 55 } 56 } 57 }
TreeViewEditingControl為編輯控件,當用戶編輯TreeViewCell時,顯示的為樹編輯控件,需要繼承TreeView,同時實現IDataGridViewEditingControl接口,實現以下方法:
1 public class TreeViewEditingControl : TreeView, IDataGridViewEditingControl 2 { 3 DataGridView dataGridView; 4 private bool valueChanged = false; 5 int rowIndex; 6 public TreeViewEditingControl() 7 { 8 try 9 { 10 //必須加Roots.tree.Nodes[0].Clone() 否則報錯 不能在多處增添或插入項,必須首先將其從當前位置移除或將其克隆 11 this.Nodes.Add(Roots.tree.Nodes[0].Clone() as TreeNode); 12 this.SelectedNode = this.Nodes[0]; 13 14 15 } 16 catch (Exception ex) 17 { 18 MessageBox.Show(ex.Message); 19 } 20 21 22 } 23 24 // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 25 // property. 26 public object EditingControlFormattedValue 27 { 28 get 29 { 30 return this.SelectedNode.Text; 31 } 32 set 33 { 34 if (value is String) 35 { 36 try 37 { 38 // This will throw an exception of the string is 39 // null, empty, or not in the format of a date. 40 this.SelectedNode = new TreeNode((String)value); 41 42 } 43 catch 44 { 45 // In the case of an exception, just use the 46 // default value so we're not left with a null 47 // value. 48 this.SelectedNode = new TreeNode(""); 49 } 50 } 51 } 52 } 53 54 // Implements the 55 // IDataGridViewEditingControl.GetEditingControlFormattedValue method. 56 public object GetEditingControlFormattedValue( 57 DataGridViewDataErrorContexts context) 58 { 59 return EditingControlFormattedValue; 60 } 61 62 // Implements the 63 // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method. 64 public void ApplyCellStyleToEditingControl( 65 DataGridViewCellStyle dataGridViewCellStyle) 66 { 67 this.Font = dataGridViewCellStyle.Font; 68 this.ForeColor = dataGridViewCellStyle.ForeColor; 69 this.BackColor = dataGridViewCellStyle.BackColor; 70 } 71 72 // Implements the IDataGridViewEditingControl.EditingControlRowIndex 73 // property. 74 public int EditingControlRowIndex 75 { 76 get 77 { 78 return rowIndex; 79 } 80 set 81 { 82 rowIndex = value; 83 } 84 } 85 86 // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 87 // method. 88 public bool EditingControlWantsInputKey( 89 Keys key, bool dataGridViewWantsInputKey) 90 { 91 // Let the TreeViewPicker handle the keys listed. 92 switch (key & Keys.KeyCode) 93 { 94 case Keys.Left: 95 case Keys.Up: 96 case Keys.Down: 97 case Keys.Right: 98 case Keys.Home: 99 case Keys.End: 100 case Keys.PageDown: 101 case Keys.PageUp: 102 return true; 103 default: 104 return !dataGridViewWantsInputKey; 105 } 106 } 107 108 // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 109 // method. 110 public void PrepareEditingControlForEdit(bool selectAll) 111 { 112 // No preparation needs to be done. 113 } 114 115 // Implements the IDataGridViewEditingControl 116 // .RepositionEditingControlOnValueChange property. 117 public bool RepositionEditingControlOnValueChange 118 { 119 get 120 { 121 return false; 122 } 123 } 124 125 // Implements the IDataGridViewEditingControl 126 // .EditingControlDataGridView property. 127 public DataGridView EditingControlDataGridView 128 { 129 get 130 { 131 return dataGridView; 132 } 133 set 134 { 135 dataGridView = value; 136 } 137 } 138 139 // Implements the IDataGridViewEditingControl 140 // .EditingControlValueChanged property. 141 public bool EditingControlValueChanged 142 { 143 get 144 { 145 return valueChanged; 146 } 147 set 148 { 149 valueChanged = value; 150 } 151 } 152 153 // Implements the IDataGridViewEditingControl 154 // .EditingPanelCursor property. 155 public Cursor EditingPanelCursor 156 { 157 get 158 { 159 return base.Cursor; 160 } 161 } 162 163 protected override void OnAfterExpand(TreeViewEventArgs e) 164 { 165 base.OnAfterExpand(e); 166 this.dataGridView.Columns[this.dataGridView.CurrentCell.ColumnIndex].Width = this.Width+10; 167 this.dataGridView.Rows[this.dataGridView.CurrentCell.RowIndex].Height = this.Height+20; 168 169 } 170 protected override void OnAfterSelect(TreeViewEventArgs e) 171 { 172 // Notify the DataGridView that the contents of the cell 173 // have changed. 174 valueChanged = true; 175 this.EditingControlDataGridView.NotifyCurrentCellDirty(true); 176 base.OnAfterSelect(e); 177 178 } 179 180 }
為了在不同類之間傳遞參數,定義一個全局靜態類:
1 /// <summary> 2 /// 靜態類的靜態屬性,用於在不同class間傳遞參數 3 /// </summary> 4 public static class Roots 5 { 6 //從前台綁定樹 7 public static TreeView tree = null; 8 }
完整代碼為:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Windows.Forms; 6 using System.ComponentModel; 7 namespace Host_Controls_in_Windows_Forms_DataGridView_Cells 8 { 9 /// <summary> 10 /// 靜態類的靜態屬性,用於在不同class間傳遞參數 11 /// </summary> 12 public static class Roots 13 { 14 //從前台綁定樹 15 public static TreeView tree = null; 16 } 17 /// <summary> 18 /// Host TreeView In DataGridView Cell 19 /// </summary> 20 public class TreeViewColumn : DataGridViewColumn 21 { 22 public TreeViewColumn() 23 : base(new TreeViewCell()) 24 { 25 } 26 [Description("Set TreeView Root in DataGridView Cell"), Category("TreeView")] 27 public TreeView _root 28 { 29 get{return Roots.tree;} 30 set{Roots.tree=value;} 31 } 32 public override DataGridViewCell CellTemplate 33 { 34 get 35 { 36 return base.CellTemplate; 37 } 38 set 39 { 40 // Ensure that the cell used for the template is a TreeViewCell. 41 if (value != null && 42 !value.GetType().IsAssignableFrom(typeof(TreeViewCell))) 43 { 44 throw new InvalidCastException("Must be a TreeViewCell"); 45 } 46 base.CellTemplate = value; 47 } 48 } 49 } 50 51 //---------------------------------------------------------------------- 52 public class TreeViewCell : DataGridViewTextBoxCell 53 { 54 55 public TreeViewCell() 56 : base() 57 { 58 59 //初始設置 60 } 61 62 public override void InitializeEditingControl(int rowIndex, object 63 initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle) 64 { 65 // Set the value of the editing control to the current cell value. 66 base.InitializeEditingControl(rowIndex, initialFormattedValue, 67 dataGridViewCellStyle); 68 TreeViewEditingControl ctl = 69 DataGridView.EditingControl as TreeViewEditingControl; 70 // Use the default row value when Value property is null. 71 if (this.Value == null) 72 { 73 74 ctl.SelectedNode =new TreeNode( this.DefaultNewRowValue.ToString()); 75 } 76 else 77 { 78 ctl.SelectedNode = new TreeNode(this.Value.ToString()); 79 } 80 } 81 82 public override Type EditType 83 { 84 get 85 { 86 // Return the type of the editing control that CalendarCell uses. 87 return typeof(TreeViewEditingControl); 88 } 89 } 90 91 public override Type ValueType 92 { 93 get 94 { 95 // Return the type of the value that CalendarCell contains. 96 return typeof(String); 97 } 98 } 99 100 public override object DefaultNewRowValue 101 { 102 get 103 { 104 // Use the current date and time as the default value. 105 return ""; 106 } 107 } 108 } 109 //----------------------------------------------------------------- 110 111 public class TreeViewEditingControl : TreeView, IDataGridViewEditingControl 112 { 113 DataGridView dataGridView; 114 private bool valueChanged = false; 115 int rowIndex; 116 public TreeViewEditingControl() 117 { 118 try 119 { 120 //必須加Roots.tree.Nodes[0].Clone() 否則報錯 不能在多處增添或插入項,必須首先將其從當前位置移除或將其克隆 121 this.Nodes.Add(Roots.tree.Nodes[0].Clone() as TreeNode); 122 this.SelectedNode = this.Nodes[0]; 123 124 125 } 126 catch (Exception ex) 127 { 128 MessageBox.Show(ex.Message); 129 } 130 131 132 } 133 134 // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 135 // property. 136 public object EditingControlFormattedValue 137 { 138 get 139 { 140 return this.SelectedNode.Text; 141 } 142 set 143 { 144 if (value is String) 145 { 146 try 147 { 148 // This will throw an exception of the string is 149 // null, empty, or not in the format of a date. 150 this.SelectedNode = new TreeNode((String)value); 151 152 } 153 catch 154 { 155 // In the case of an exception, just use the 156 // default value so we're not left with a null 157 // value. 158 this.SelectedNode = new TreeNode(""); 159 } 160 } 161 } 162 } 163 164 // Implements the 165 // IDataGridViewEditingControl.GetEditingControlFormattedValue method. 166 public object GetEditingControlFormattedValue( 167 DataGridViewDataErrorContexts context) 168 { 169 return EditingControlFormattedValue; 170 } 171 172 // Implements the 173 // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method. 174 public void ApplyCellStyleToEditingControl( 175 DataGridViewCellStyle dataGridViewCellStyle) 176 { 177 this.Font = dataGridViewCellStyle.Font; 178 this.ForeColor = dataGridViewCellStyle.ForeColor; 179 this.BackColor = dataGridViewCellStyle.BackColor; 180 } 181 182 // Implements the IDataGridViewEditingControl.EditingControlRowIndex 183 // property. 184 public int EditingControlRowIndex 185 { 186 get 187 { 188 return rowIndex; 189 } 190 set 191 { 192 rowIndex = value; 193 } 194 } 195 196 // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 197 // method. 198 public bool EditingControlWantsInputKey( 199 Keys key, bool dataGridViewWantsInputKey) 200 { 201 // Let the TreeViewPicker handle the keys listed. 202 switch (key & Keys.KeyCode) 203 { 204 case Keys.Left: 205 case Keys.Up: 206 case Keys.Down: 207 case Keys.Right: 208 case Keys.Home: 209 case Keys.End: 210 case Keys.PageDown: 211 case Keys.PageUp: 212 return true; 213 default: 214 return !dataGridViewWantsInputKey; 215 } 216 } 217 218 // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 219 // method. 220 public void PrepareEditingControlForEdit(bool selectAll) 221 { 222 // No preparation needs to be done. 223 } 224 225 // Implements the IDataGridViewEditingControl 226 // .RepositionEditingControlOnValueChange property. 227 public bool RepositionEditingControlOnValueChange 228 { 229 get 230 { 231 return false; 232 } 233 } 234 235 // Implements the IDataGridViewEditingControl 236 // .EditingControlDataGridView property. 237 public DataGridView EditingControlDataGridView 238 { 239 get 240 { 241 return dataGridView; 242 } 243 set 244 { 245 dataGridView = value; 246 } 247 } 248 249 // Implements the IDataGridViewEditingControl 250 // .EditingControlValueChanged property. 251 public bool EditingControlValueChanged 252 { 253 get 254 { 255 return valueChanged; 256 } 257 set 258 { 259 valueChanged = value; 260 } 261 } 262 263 // Implements the IDataGridViewEditingControl 264 // .EditingPanelCursor property. 265 public Cursor EditingPanelCursor 266 { 267 get 268 { 269 return base.Cursor; 270 } 271 } 272 273 protected override void OnAfterExpand(TreeViewEventArgs e) 274 { 275 base.OnAfterExpand(e); 276 this.dataGridView.Columns[this.dataGridView.CurrentCell.ColumnIndex].Width = this.Width+10; 277 this.dataGridView.Rows[this.dataGridView.CurrentCell.RowIndex].Height = this.Height+20; 278 279 } 280 protected override void OnAfterSelect(TreeViewEventArgs e) 281 { 282 // Notify the DataGridView that the contents of the cell 283 // have changed. 284 valueChanged = true; 285 this.EditingControlDataGridView.NotifyCurrentCellDirty(true); 286 base.OnAfterSelect(e); 287 288 } 289 290 } 291 292 293 294 } View Code當編輯無誤後,可以在添加列的時候看到TreeViewColumn類型。此類型暴露出一個_root屬性,可以綁定外部的一個帶數據的TreeView。
運行代碼,單擊單元格,進入編輯狀態,可以看到如下界面: