DataGridView自定义ComboBox实现错误,共享同一个值
DataGridView custom ComboBox implementation error, sharing same value
这是我用来创建自己的自定义组合框列的代码,我的组合框有点特殊,它在内部显示树视图(这里是代码页 http://www.brad-smith.info/blog/projects/dropdown-controls)。
新的组合框列工作得很好,除了一件事,如果我 select 例如从组合框导航到旁边的单元格(这是文本框单元格),然后导航到第二行中的相同组合框列和 select 第二个组合框中的另一个项目然后一切都很好,但是如果我从一个组合框直接导航到它下面的那个并且 select 一个项目然后第一个组合框将 select 与第二个相同的值组合框。
有什么帮助吗?
public class DataGridViewTreeComboBoxColumn : DataGridViewComboBoxColumn
{
public DataGridViewTreeComboBoxColumn() : base()
{
base.CellTemplate = new TreeComboBoxCell();
}
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(TreeComboBoxCell)))
{
throw new InvalidCastException("Must be a CalendarCell");
}
base.CellTemplate = value;
}
}
}
public class TreeComboBoxCell : DataGridViewComboBoxCell
{
public TreeComboBoxCell()
: base()
{
}
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);
TreeComboBoxEditingControl ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
if (Value != null)
ctl.SelectedNode = ctl.AllNodes.ToList().First(x => x.Tag != null && x.Tag.Equals(Value));
ctl.SelectedNodeChanged += Ctl_SelectedNodeChanged;
}
public override object Clone()
{
TreeComboBoxCell dataGridViewCell = base.Clone() as TreeComboBoxCell;
if (dataGridViewCell != null)
{
}
return dataGridViewCell;
}
private void Ctl_SelectedNodeChanged(object sender, EventArgs e)
{
if (((TreeComboBoxEditingControl)sender).SelectedNode != null)
Value = ((TreeComboBoxEditingControl)sender).SelectedNode.Tag;
}
public override Type EditType
{
get
{
// Return the type of the editing control that CalendarCell uses.
return typeof(TreeComboBoxEditingControl);
}
}
public override Type ValueType
{
get
{
// Return the type of the value that CalendarCell contains.
return typeof(Object);
}
}
public override object DefaultNewRowValue
{
get
{
// Use the current date and time as the default value.
return 0;
}
}
}
public class TreeComboBoxEditingControl : ComboTreeBox, IDataGridViewEditingControl
{
DataGridView dataGridView;
private bool valueChanged = false;
int rowIndex;
public TreeComboBoxEditingControl()
{
this.TabStop = false;
}
public void SetItems(DataGridViewComboBoxCell.ObjectCollection items)
{
if (Nodes != null && Nodes.Count > 0)
return;
Action<ComboTreeNodeCollection> addNodesHelper = nodes => {
foreach (IGrouping<object, TreeComboBoxItem> group in items.Cast<TreeComboBoxItem>().GroupBy(x => x.Group).ToList())
{
ComboTreeNode parent = nodes.Add(group.Key.ToString());
foreach (TreeComboBoxItem item in group)
{
parent.Nodes.Add(item.Display).Tag = item.Value;
}
}
};
Action<ComboTreeBox> addNodes = ctb => {
addNodesHelper(ctb.Nodes);
ctb.Sort();
};
addNodes(this);
}
// Implements the IDataGridViewEditingControl.EditingControlFormattedValue
// property.
public object EditingControlFormattedValue
{
get
{
return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting);
}
set
{
}
}
// Implements the
// IDataGridViewEditingControl.GetEditingControlFormattedValue method.
public object GetEditingControlFormattedValue(
DataGridViewDataErrorContexts context)
{
if (this.SelectedNode == null)
return null;
return this.SelectedNode.Tag;
}
// Implements the
// IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)
{
}
// Implements the IDataGridViewEditingControl.EditingControlRowIndex
// property.
public int EditingControlRowIndex
{
get
{
return rowIndex;
}
set
{
rowIndex = value;
}
}
// Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
// method.
public bool EditingControlWantsInputKey(
Keys key, bool dataGridViewWantsInputKey)
{
// Let the DateTimePicker handle the keys listed.
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
// Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
// method.
public void PrepareEditingControlForEdit(bool selectAll)
{
// No preparation needs to be done.
}
// Implements the IDataGridViewEditingControl
// .RepositionEditingControlOnValueChange property.
public bool RepositionEditingControlOnValueChange
{
get
{
return false;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlDataGridView property.
public DataGridView EditingControlDataGridView
{
get
{
return dataGridView;
}
set
{
dataGridView = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlValueChanged property.
public bool EditingControlValueChanged
{
get
{
return valueChanged;
}
set
{
valueChanged = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingPanelCursor property.
public Cursor EditingPanelCursor
{
get
{
return base.Cursor;
}
}
protected override void OnSelectedNodeChanged(EventArgs e)
{
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnSelectedNodeChanged(e);
}
}
我认为你应该在 TreeComboBoxCell
class 中添加以下内容:
public override void DetachEditingControl()
{
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
if (ctl != null)
ctl.SelectedNodeChanged -= Ctl_SelectedNodeChanged;
base.DetachEditingControl();
}
同样修改如下方法为:
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
ctl.SelectedNode = initialFormattedValue != null ? ctl.AllNodes.FirstOrDefault(x => Equals(x.Tag, initialFormattedValue)) : null;
ctl.SelectedNodeChanged += Ctl_SelectedNodeChanged;
}
同时删除 ValueType
覆盖并让 DefaultNewRowValue
return null
而不是 0
。
但我看到的主要问题是您的单元格和编辑器 classes 之间的同步。单元格 class 使用 Items
集合中的 TreeComboBoxItem
个对象,但编辑器 GetEditingControlFormattedValue
return 是一个对象,实际上是 TreeComboBoxItem.Value
属性。这样基本单元格 class 就无法正确翻译它。我不确定您是否应该从 DataGridViewComboBoxCell
继承,因为它需要一个 ComboBox
编辑器来覆盖您不处理的许多内容。最好从 DataGridViewCell
或 DataGridViewTextBoxCell
继承,就像您的代码所基于的 MSDN 示例一样。至少您可以尝试添加以下覆盖以使您的单元格 class 实现与您当前的编辑器实现相匹配:
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value != null)
foreach (TreeComboBoxItem item in Items)
if (Equals(item.Value, value)) return item.Display;
return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
}
public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
{
return formattedValue;
}
EDIT 好的,我不确定到底是什么不起作用,可能是用法。这是一个完整的工作代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace System.Windows.Forms
{
public class TreeComboBoxItem
{
public object Group { get; set; }
public object Value { get; set; }
private string display;
public string Display { get { return display ?? (Value != null ? Value.ToString() : null); } set { display = value; } }
}
public class DataGridViewTreeComboBoxColumn : DataGridViewComboBoxColumn
{
public DataGridViewTreeComboBoxColumn()
{
base.CellTemplate = new TreeComboBoxCell();
}
public override DataGridViewCell CellTemplate
{
get { return base.CellTemplate; }
set { base.CellTemplate = (TreeComboBoxCell)value; }
}
}
public class TreeComboBoxCell : DataGridViewComboBoxCell
{
public TreeComboBoxCell() { }
public override Type EditType { get { return typeof(TreeComboBoxEditingControl); } }
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
ctl.SelectedNode = Value != null ? ctl.AllNodes.FirstOrDefault(x => Equals(x.Tag, Value)) : null;
ctl.SelectedNodeChanged += OnEditorSelectedNodeChanged;
}
public override void DetachEditingControl()
{
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
if (ctl != null) ctl.SelectedNodeChanged -= OnEditorSelectedNodeChanged;
base.DetachEditingControl();
}
public override object Clone()
{
var dataGridViewCell = base.Clone() as TreeComboBoxCell;
if (dataGridViewCell != null)
{
}
return dataGridViewCell;
}
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value != null)
{
foreach (TreeComboBoxItem item in Items)
if (Equals(item.Value, value)) return (context & DataGridViewDataErrorContexts.Formatting) != 0 ? item.Display : value;
}
return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
}
public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
{
return formattedValue;
}
private void OnEditorSelectedNodeChanged(object sender, EventArgs e)
{
var selectedNode = ((TreeComboBoxEditingControl)sender).SelectedNode;
Value = selectedNode != null ? selectedNode.Tag : null;
}
}
public class TreeComboBoxEditingControl : ComboTreeBox, IDataGridViewEditingControl
{
public TreeComboBoxEditingControl() { TabStop = false; }
public DataGridView EditingControlDataGridView { get; set; }
public int EditingControlRowIndex { get; set; }
public bool EditingControlValueChanged { get; set; }
public bool RepositionEditingControlOnValueChange { get { return false; } }
public Cursor EditingPanelCursor { get { return Cursor; } }
public void SetItems(DataGridViewComboBoxCell.ObjectCollection items)
{
if (Nodes.Count > 0) return;
foreach (var group in items.Cast<TreeComboBoxItem>().GroupBy(x => x.Group))
{
var parent = Nodes.Add(group.Key.ToString());
foreach (var item in group)
parent.Nodes.Add(item.Display).Tag = item.Value;
}
Sort();
}
protected override void OnSelectedNodeChanged(EventArgs e)
{
EditingControlValueChanged = true;
EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnSelectedNodeChanged(e);
}
public object EditingControlFormattedValue
{
get { return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); }
set
{
}
}
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
if (SelectedNode == null) return null;
return (context & DataGridViewDataErrorContexts.Formatting) != 0 ? SelectedNode.Text : SelectedNode.Tag;
}
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
BackColor = dataGridViewCellStyle.BackColor;
ForeColor = dataGridViewCellStyle.ForeColor;
}
public void PrepareEditingControlForEdit(bool selectAll)
{
}
public bool EditingControlWantsInputKey(Keys key, bool dataGridViewWantsInputKey)
{
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
}
}
namespace Tests
{
class Parent
{
public string Name { get; set; }
public override string ToString() { return Name; }
}
class Child
{
public Parent Parent { get; set; }
public string Name { get; set; }
}
class TestForm : Form
{
public TestForm()
{
var parents = Enumerable.Range(1, 6).Select(i => new Parent { Name = "Parent " + i }).ToList();
var childen = Enumerable.Range(1, 10).Select(i => new Child { Parent = parents[i % parents.Count], Name = "Child " + i }).ToList();
var items = parents.Select((parent, i) => new TreeComboBoxItem { Value = parent, Group = "Group " + ((i % 2) + 1) }).ToArray();
var dg = new DataGridView { Dock = DockStyle.Fill, Parent = this, AutoGenerateColumns = false };
var c1 = new DataGridViewTreeComboBoxColumn { DataPropertyName = "Parent", HeaderText = "Parent" };
c1.Items.AddRange(items);
var c2 = new DataGridViewTextBoxColumn { DataPropertyName = "Name", HeaderText = "Name" };
dg.Columns.AddRange(c1, c2);
dg.DataSource = new BindingList<Child>(childen);
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
}
这是我用来创建自己的自定义组合框列的代码,我的组合框有点特殊,它在内部显示树视图(这里是代码页 http://www.brad-smith.info/blog/projects/dropdown-controls)。 新的组合框列工作得很好,除了一件事,如果我 select 例如从组合框导航到旁边的单元格(这是文本框单元格),然后导航到第二行中的相同组合框列和 select 第二个组合框中的另一个项目然后一切都很好,但是如果我从一个组合框直接导航到它下面的那个并且 select 一个项目然后第一个组合框将 select 与第二个相同的值组合框。
有什么帮助吗?
public class DataGridViewTreeComboBoxColumn : DataGridViewComboBoxColumn
{
public DataGridViewTreeComboBoxColumn() : base()
{
base.CellTemplate = new TreeComboBoxCell();
}
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(TreeComboBoxCell)))
{
throw new InvalidCastException("Must be a CalendarCell");
}
base.CellTemplate = value;
}
}
}
public class TreeComboBoxCell : DataGridViewComboBoxCell
{
public TreeComboBoxCell()
: base()
{
}
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);
TreeComboBoxEditingControl ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
if (Value != null)
ctl.SelectedNode = ctl.AllNodes.ToList().First(x => x.Tag != null && x.Tag.Equals(Value));
ctl.SelectedNodeChanged += Ctl_SelectedNodeChanged;
}
public override object Clone()
{
TreeComboBoxCell dataGridViewCell = base.Clone() as TreeComboBoxCell;
if (dataGridViewCell != null)
{
}
return dataGridViewCell;
}
private void Ctl_SelectedNodeChanged(object sender, EventArgs e)
{
if (((TreeComboBoxEditingControl)sender).SelectedNode != null)
Value = ((TreeComboBoxEditingControl)sender).SelectedNode.Tag;
}
public override Type EditType
{
get
{
// Return the type of the editing control that CalendarCell uses.
return typeof(TreeComboBoxEditingControl);
}
}
public override Type ValueType
{
get
{
// Return the type of the value that CalendarCell contains.
return typeof(Object);
}
}
public override object DefaultNewRowValue
{
get
{
// Use the current date and time as the default value.
return 0;
}
}
}
public class TreeComboBoxEditingControl : ComboTreeBox, IDataGridViewEditingControl
{
DataGridView dataGridView;
private bool valueChanged = false;
int rowIndex;
public TreeComboBoxEditingControl()
{
this.TabStop = false;
}
public void SetItems(DataGridViewComboBoxCell.ObjectCollection items)
{
if (Nodes != null && Nodes.Count > 0)
return;
Action<ComboTreeNodeCollection> addNodesHelper = nodes => {
foreach (IGrouping<object, TreeComboBoxItem> group in items.Cast<TreeComboBoxItem>().GroupBy(x => x.Group).ToList())
{
ComboTreeNode parent = nodes.Add(group.Key.ToString());
foreach (TreeComboBoxItem item in group)
{
parent.Nodes.Add(item.Display).Tag = item.Value;
}
}
};
Action<ComboTreeBox> addNodes = ctb => {
addNodesHelper(ctb.Nodes);
ctb.Sort();
};
addNodes(this);
}
// Implements the IDataGridViewEditingControl.EditingControlFormattedValue
// property.
public object EditingControlFormattedValue
{
get
{
return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting);
}
set
{
}
}
// Implements the
// IDataGridViewEditingControl.GetEditingControlFormattedValue method.
public object GetEditingControlFormattedValue(
DataGridViewDataErrorContexts context)
{
if (this.SelectedNode == null)
return null;
return this.SelectedNode.Tag;
}
// Implements the
// IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)
{
}
// Implements the IDataGridViewEditingControl.EditingControlRowIndex
// property.
public int EditingControlRowIndex
{
get
{
return rowIndex;
}
set
{
rowIndex = value;
}
}
// Implements the IDataGridViewEditingControl.EditingControlWantsInputKey
// method.
public bool EditingControlWantsInputKey(
Keys key, bool dataGridViewWantsInputKey)
{
// Let the DateTimePicker handle the keys listed.
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
// Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit
// method.
public void PrepareEditingControlForEdit(bool selectAll)
{
// No preparation needs to be done.
}
// Implements the IDataGridViewEditingControl
// .RepositionEditingControlOnValueChange property.
public bool RepositionEditingControlOnValueChange
{
get
{
return false;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlDataGridView property.
public DataGridView EditingControlDataGridView
{
get
{
return dataGridView;
}
set
{
dataGridView = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingControlValueChanged property.
public bool EditingControlValueChanged
{
get
{
return valueChanged;
}
set
{
valueChanged = value;
}
}
// Implements the IDataGridViewEditingControl
// .EditingPanelCursor property.
public Cursor EditingPanelCursor
{
get
{
return base.Cursor;
}
}
protected override void OnSelectedNodeChanged(EventArgs e)
{
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnSelectedNodeChanged(e);
}
}
我认为你应该在 TreeComboBoxCell
class 中添加以下内容:
public override void DetachEditingControl()
{
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
if (ctl != null)
ctl.SelectedNodeChanged -= Ctl_SelectedNodeChanged;
base.DetachEditingControl();
}
同样修改如下方法为:
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
ctl.SelectedNode = initialFormattedValue != null ? ctl.AllNodes.FirstOrDefault(x => Equals(x.Tag, initialFormattedValue)) : null;
ctl.SelectedNodeChanged += Ctl_SelectedNodeChanged;
}
同时删除 ValueType
覆盖并让 DefaultNewRowValue
return null
而不是 0
。
但我看到的主要问题是您的单元格和编辑器 classes 之间的同步。单元格 class 使用 Items
集合中的 TreeComboBoxItem
个对象,但编辑器 GetEditingControlFormattedValue
return 是一个对象,实际上是 TreeComboBoxItem.Value
属性。这样基本单元格 class 就无法正确翻译它。我不确定您是否应该从 DataGridViewComboBoxCell
继承,因为它需要一个 ComboBox
编辑器来覆盖您不处理的许多内容。最好从 DataGridViewCell
或 DataGridViewTextBoxCell
继承,就像您的代码所基于的 MSDN 示例一样。至少您可以尝试添加以下覆盖以使您的单元格 class 实现与您当前的编辑器实现相匹配:
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value != null)
foreach (TreeComboBoxItem item in Items)
if (Equals(item.Value, value)) return item.Display;
return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
}
public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
{
return formattedValue;
}
EDIT 好的,我不确定到底是什么不起作用,可能是用法。这是一个完整的工作代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace System.Windows.Forms
{
public class TreeComboBoxItem
{
public object Group { get; set; }
public object Value { get; set; }
private string display;
public string Display { get { return display ?? (Value != null ? Value.ToString() : null); } set { display = value; } }
}
public class DataGridViewTreeComboBoxColumn : DataGridViewComboBoxColumn
{
public DataGridViewTreeComboBoxColumn()
{
base.CellTemplate = new TreeComboBoxCell();
}
public override DataGridViewCell CellTemplate
{
get { return base.CellTemplate; }
set { base.CellTemplate = (TreeComboBoxCell)value; }
}
}
public class TreeComboBoxCell : DataGridViewComboBoxCell
{
public TreeComboBoxCell() { }
public override Type EditType { get { return typeof(TreeComboBoxEditingControl); } }
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
ctl.SetItems(Items);
ctl.SelectedNode = Value != null ? ctl.AllNodes.FirstOrDefault(x => Equals(x.Tag, Value)) : null;
ctl.SelectedNodeChanged += OnEditorSelectedNodeChanged;
}
public override void DetachEditingControl()
{
var ctl = DataGridView.EditingControl as TreeComboBoxEditingControl;
if (ctl != null) ctl.SelectedNodeChanged -= OnEditorSelectedNodeChanged;
base.DetachEditingControl();
}
public override object Clone()
{
var dataGridViewCell = base.Clone() as TreeComboBoxCell;
if (dataGridViewCell != null)
{
}
return dataGridViewCell;
}
protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
{
if (value != null)
{
foreach (TreeComboBoxItem item in Items)
if (Equals(item.Value, value)) return (context & DataGridViewDataErrorContexts.Formatting) != 0 ? item.Display : value;
}
return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
}
public override object ParseFormattedValue(object formattedValue, DataGridViewCellStyle cellStyle, TypeConverter formattedValueTypeConverter, TypeConverter valueTypeConverter)
{
return formattedValue;
}
private void OnEditorSelectedNodeChanged(object sender, EventArgs e)
{
var selectedNode = ((TreeComboBoxEditingControl)sender).SelectedNode;
Value = selectedNode != null ? selectedNode.Tag : null;
}
}
public class TreeComboBoxEditingControl : ComboTreeBox, IDataGridViewEditingControl
{
public TreeComboBoxEditingControl() { TabStop = false; }
public DataGridView EditingControlDataGridView { get; set; }
public int EditingControlRowIndex { get; set; }
public bool EditingControlValueChanged { get; set; }
public bool RepositionEditingControlOnValueChange { get { return false; } }
public Cursor EditingPanelCursor { get { return Cursor; } }
public void SetItems(DataGridViewComboBoxCell.ObjectCollection items)
{
if (Nodes.Count > 0) return;
foreach (var group in items.Cast<TreeComboBoxItem>().GroupBy(x => x.Group))
{
var parent = Nodes.Add(group.Key.ToString());
foreach (var item in group)
parent.Nodes.Add(item.Display).Tag = item.Value;
}
Sort();
}
protected override void OnSelectedNodeChanged(EventArgs e)
{
EditingControlValueChanged = true;
EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnSelectedNodeChanged(e);
}
public object EditingControlFormattedValue
{
get { return GetEditingControlFormattedValue(DataGridViewDataErrorContexts.Formatting); }
set
{
}
}
public object GetEditingControlFormattedValue(DataGridViewDataErrorContexts context)
{
if (SelectedNode == null) return null;
return (context & DataGridViewDataErrorContexts.Formatting) != 0 ? SelectedNode.Text : SelectedNode.Tag;
}
public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
{
BackColor = dataGridViewCellStyle.BackColor;
ForeColor = dataGridViewCellStyle.ForeColor;
}
public void PrepareEditingControlForEdit(bool selectAll)
{
}
public bool EditingControlWantsInputKey(Keys key, bool dataGridViewWantsInputKey)
{
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
}
}
namespace Tests
{
class Parent
{
public string Name { get; set; }
public override string ToString() { return Name; }
}
class Child
{
public Parent Parent { get; set; }
public string Name { get; set; }
}
class TestForm : Form
{
public TestForm()
{
var parents = Enumerable.Range(1, 6).Select(i => new Parent { Name = "Parent " + i }).ToList();
var childen = Enumerable.Range(1, 10).Select(i => new Child { Parent = parents[i % parents.Count], Name = "Child " + i }).ToList();
var items = parents.Select((parent, i) => new TreeComboBoxItem { Value = parent, Group = "Group " + ((i % 2) + 1) }).ToArray();
var dg = new DataGridView { Dock = DockStyle.Fill, Parent = this, AutoGenerateColumns = false };
var c1 = new DataGridViewTreeComboBoxColumn { DataPropertyName = "Parent", HeaderText = "Parent" };
c1.Items.AddRange(items);
var c2 = new DataGridViewTextBoxColumn { DataPropertyName = "Name", HeaderText = "Name" };
dg.Columns.AddRange(c1, c2);
dg.DataSource = new BindingList<Child>(childen);
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new TestForm());
}
}
}