我们可以自定义DataGridView的DataGridViewColumn来实现自定义的列,下面介绍一下如何通过扩展DataGridViewColumn来实现一个TreeViewColumn
1 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 _root12 {13 get{return Roots.tree;}14 set{Roots.tree=value;}15 }16 public override DataGridViewCell CellTemplate17 {18 get19 {20 return base.CellTemplate;21 }22 set23 {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 }
2 TreeViewCell类
上面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, object12 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 else26 {27 ctl.SelectedNode = new TreeNode(this.Value.ToString());28 }29 }30 31 public override Type EditType32 {33 get34 {35 // Return the type of the editing control that CalendarCell uses.36 return typeof(TreeViewEditingControl);37 }38 }39 40 public override Type ValueType41 {42 get43 {44 // Return the type of the value that CalendarCell contains.45 return typeof(String);46 }47 }48 49 public override object DefaultNewRowValue50 {51 get52 {53 // Use the current date and time as the default value.54 return "";55 }56 }57 }
3 TreeViewEditingControl类
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 IDataGridViewEditingControl116 // .RepositionEditingControlOnValueChange property.117 public bool RepositionEditingControlOnValueChange118 {119 get120 {121 return false;122 }123 }124 125 // Implements the IDataGridViewEditingControl126 // .EditingControlDataGridView property.127 public DataGridView EditingControlDataGridView128 {129 get130 {131 return dataGridView;132 }133 set134 {135 dataGridView = value;136 }137 }138 139 // Implements the IDataGridViewEditingControl140 // .EditingControlValueChanged property.141 public bool EditingControlValueChanged142 {143 get144 {145 return valueChanged;146 }147 set148 {149 valueChanged = value;150 }151 }152 153 // Implements the IDataGridViewEditingControl154 // .EditingPanelCursor property.155 public Cursor EditingPanelCursor156 {157 get158 {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 cell173 // 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 Roots5 {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 DefaultNewRowValue101 {102 get103 {104 // Use the current date and time as the default value.105 return "";106 }107 }108 }109 //-----------------------------------------------------------------110 111 public class TreeViewEditingControl : TreeView, IDataGridViewEditingControl112 {113 DataGridView dataGridView;114 private bool valueChanged = false;115 int rowIndex;116 public TreeViewEditingControl()117 {118 try119 {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 EditingControlFormattedValue137 {138 get139 {140 return this.SelectedNode.Text;141 }142 set143 {144 if (value is String)145 {146 try147 {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 catch154 {155 // In the case of an exception, just use the 156 // default value so we're not left with a null157 // 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 EditingControlRowIndex185 {186 get187 {188 return rowIndex;189 }190 set191 {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 IDataGridViewEditingControl226 // .RepositionEditingControlOnValueChange property.227 public bool RepositionEditingControlOnValueChange228 {229 get230 {231 return false;232 }233 }234 235 // Implements the IDataGridViewEditingControl236 // .EditingControlDataGridView property.237 public DataGridView EditingControlDataGridView238 {239 get240 {241 return dataGridView;242 }243 set244 {245 dataGridView = value;246 }247 }248 249 // Implements the IDataGridViewEditingControl250 // .EditingControlValueChanged property.251 public bool EditingControlValueChanged252 {253 get254 {255 return valueChanged;256 }257 set258 {259 valueChanged = value;260 }261 }262 263 // Implements the IDataGridViewEditingControl264 // .EditingPanelCursor property.265 public Cursor EditingPanelCursor266 {267 get268 {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 cell283 // have changed.284 valueChanged = true;285 this.EditingControlDataGridView.NotifyCurrentCellDirty(true);286 base.OnAfterSelect(e);287 288 }289 290 }291 292 293 294 }
当编辑无误后,可以在添加列的时候看到TreeViewColumn类型。此类型暴露出一个_root属性,可以绑定外部的一个带数据的TreeView。
运行代码,单击单元格,进入编辑状态,可以看到如下界面: