DataGridView自定义组合框实现错误,共享相同的值

本文关键字:共享 错误 自定义 组合 实现 DataGridView | 更新日期: 2023-09-27 18:10:42

这是我的代码,我用来创建自己的自定义组合框列,我的组合框是一种特殊的,显示treeview里面(这里是代码页http://www.brad-smith.info/blog/projects/dropdown-controls)。新组合框列适用除了一件事,如果我从下拉列表框选择例如并导航到旁边的细胞细胞(文本框),然后导航到相同的下拉列表框列在第二行,然后从第二个下拉列表框选择另一项都是很好,但是如果我从一个组合框导航到一个直接和选择一个项目然后第一个下拉列表框选择第二个下拉列表框的值相同。

请帮忙好吗?

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);
    }
}

DataGridView自定义组合框实现错误,共享相同的值

我认为您应该在您的TreeComboBoxCell类中添加以下内容:

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返回null而不是0

但我看到的主要问题是你的单元格和编辑器类之间的同步。单元格类在Items集合中使用TreeComboBoxItem对象,但编辑器GetEditingControlFormattedValue返回一个对象,这实际上是TreeComboBoxItem.Value属性。这样基单元格类就不能正确地翻译它。我不确定你甚至应该从DataGridViewComboBoxCell继承,因为它期望在许多你不处理的覆盖ComboBox编辑器。从DataGridViewCellDataGridViewTextBoxCell继承可能会更好,就像在MSDN示例中您的代码所基于的那样。至少您可以尝试添加以下覆盖,以使单元格类实现与当前编辑器实现匹配:

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());
        }
    }
}