【问题标题】:Creating AutoComplete column in DataGridView在 DataGridView 中创建自动完成列
【发布时间】:2017-10-30 20:51:57
【问题描述】:

我正在尝试使用 C# 在 WinForms 中使用 DataGridView 创建一个自动完成列。我已经设法使用 DataGridView 的EditingControlShowing 事件让它工作并且工作正常。

但是,按照正常的自动完成文本框,过滤后的列表仅显示基于“开始于”的数据过滤。为了解决这个问题,我使用了来自here 的自动完成文本框,它允许使用自定义列表框进行子字符串搜索。

以这个自定义控件为基础,我创建了一个继承DataGridViewColumn的自定义控件。问题是 ListBox 控件不显示与 gridview 单元格内联。这是代码 -

public class DataGridViewAutoCompleteColumn : DataGridViewColumn
{
    public DataGridViewAutoCompleteColumn()
        : base(new DataGridViewAutoCompleteCell())
    {
    }

    public override DataGridViewCell CellTemplate
    {
        get
        {
            return base.CellTemplate;
        }
        set
        {
            // Ensure that the cell used for the template is a DataGridViewAutoCompleteCell.
            if (value != null &&
                !value.GetType().IsAssignableFrom(typeof(DataGridViewAutoCompleteCell)))
            {
                throw new InvalidCastException("Must be a DataGridViewAutoCompleteCell");
            }
            base.CellTemplate = value;
        }
    }


}

public class DataGridViewAutoCompleteCell : DataGridViewTextBoxCell
{

    public DataGridViewAutoCompleteCell()
        : base()
    {
        // Use the short date format.
        this.Style.Format = "d";
    }

    public override void InitializeEditingControl(int rowIndex, object
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Set the value of the editing control to the current cell value.
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        AutoCompleteEditingControl ctl = DataGridView.EditingControl as AutoCompleteEditingControl;
        ctl.AutoCompleteList = this.AutoCompleteList;
        // Use the default row value when Value property is null.
        if (this.Value == null)
        {
            ctl.Text = (string)this.DefaultNewRowValue;
        }
        else
        {
            ctl.Text = (string)this.Value;
        }
    }

    public override Type EditType
    {
        get
        {
            // Return the type of the editing control that DataGridViewAutoCompleteCell uses.
            return typeof(AutoCompleteEditingControl);
        }
    }

    public override Type ValueType
    {
        get
        {
            // Return the type of the value that DataGridViewAutoCompleteCell contains.

            return typeof(String);
        }
    }

    public override object DefaultNewRowValue
    {
        get
        {
            // Use the current date and time as the default value.
            return string.Empty;
           // return DateTime.Now;
        }
    }

    public List<String> AutoCompleteList { get; set; }
}

class AutoCompleteEditingControl : AutoCompleteTextbox, IDataGridViewEditingControl
{
    DataGridView dataGridView;
    private bool valueChanged = false;
    int rowIndex;

    public AutoCompleteEditingControl()
    {

    }

    // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
    // property.
    public object EditingControlFormattedValue
    {
        get
        {
            return this.Text;
        }
        set
        {
            if (value is String)
            {
                try
                {
                    // This will throw an exception of the string is 
                    // null, empty, or not in the format of a date.
                    this.Text = (String)value;
                }
                catch
                {
                    // In the case of an exception, just use the 
                    // default value so we're not left with a null
                    // value.
                    this.Text = String.Empty;
                }
            }
        }
    }

    // Implements the 
    // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
    public object GetEditingControlFormattedValue(
        DataGridViewDataErrorContexts context)
    {
        return EditingControlFormattedValue;
    }

    // Implements the 
    // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
    public void ApplyCellStyleToEditingControl(
        DataGridViewCellStyle dataGridViewCellStyle)
    {
        this.Font = dataGridViewCellStyle.Font;
        this.ForeColor = dataGridViewCellStyle.ForeColor;
        this.BackColor = dataGridViewCellStyle.BackColor;
    }

    // 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 OnTextChanged(EventArgs eventargs)
    {
        // Notify the DataGridView that the contents of the cell
        // have changed.
        valueChanged = true;
        this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
        base.OnTextChanged(eventargs);
    }
}

请告知我在这里做错了什么。

【问题讨论】:

    标签: c# winforms datagridview autocomplete datagridviewcolumn


    【解决方案1】:

    我现在已经完成了其中的一些,虽然它们确实很强大,但它比实际应该的要复杂一些。

    首先,您没有在列级别的任何地方传递 AutoCompleteList。这意味着它需要在每个单元格上进行设置,这可能很有用,但这不是数据网格通常的功能。所以它需要是列类的一个属性,因为这是你可以设置它的地方。

    此外,如果 Column 类具有任何自定义属性,则它需要覆盖 Clone() 方法以维护这些属性。实现中的某些东西意味着没有这个它们就无法工作。 您可能希望公开 CaseSensitive 和 MinTypedCharacters 的属性。

    public class DataGridViewAutoCompleteColumn : DataGridViewColumn
    {
        public DataGridViewAutoCompleteColumn()
            : base(new DataGridViewAutoCompleteCell())
        {
        }
    
        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }
            set
            {
                // Ensure that the cell used for the template is a DataGridViewAutoCompleteCell.
                if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(DataGridViewAutoCompleteCell)))
                {
                    throw new InvalidCastException("Must be a DataGridViewAutoCompleteCell");
                }
                base.CellTemplate = value;
            }
        }
    
        [Browsable(true)]
        public List<string> AutoCompleteList
        {
            get; set;
        }
    
        [Browsable(true)]
        public int MinTypedCharacters { get; set; }
        [Browsable(true)]
        public bool CaseSensitive { get; set; }
    
        public override object Clone()
        {
            DataGridViewAutoCompleteColumn clone = (DataGridViewAutoCompleteColumn)base.Clone();
            clone.AutoCompleteList = this.AutoCompleteList;
            clone.MinTypedCharacters = this.MinTypedCharacters;
            clone.CaseSensitive = this.CaseSensitive;
            return clone;
        }
    }
    

    在单元类上,我们仍然可以通过修改单元级列表的 AutoComplete 列表属性来使用单元级列表作为覆盖。

    private List<string> _autoCompleteList;
    public List<String> AutoCompleteList
    {
        get
        {
            if (_autoCompleteList == null)
                return ((DataGridViewAutoCompleteColumn)this.OwningColumn).AutoCompleteList;
            else
                return
                    _autoCompleteList;
        }
        set
        {
            _autoCompleteList = value;
        }
    }
    

    然后您需要在 InitializeEditingControl 中传递这些设置。您可以像这样访问列对象:

    DataGridViewAutoCompleteColumn col = (DataGridViewAutoCompleteColumn)this.OwningColumn;
    

    接下来,DataGridViewCell.Value get 访问器中存在一个错误,这意味着您无法在 InitializeEditingControl() 方法中安全地使用它。它有时会尝试使用无效的 rowIndex。您应该改用 GetValue(rowIndex)。

    public override void InitializeEditingControl(int rowIndex, object
        initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
    {
        // Set the value of the editing control to the current cell value.
        base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
        AutoCompleteEditingControl ctl = DataGridView.EditingControl as AutoCompleteEditingControl;
        ctl.AutoCompleteList = this.AutoCompleteList;
        // Use the default row value when Value property is null.
        if (this.Value == null)
        {
            ctl.Text = (string)this.DefaultNewRowValue;
        }
        else
        {
            ctl.Text = (string)this.GetValue(rowIndex);  // this line can't use this.Value
        }
    }
    

    其他问题,包括你第一次问的问题都在 AutoCompleteTextBox 类中(你还没有为此发布代码)

    它的 ParentForm 方法在创建控件时创建空引用。
    但比这更基本的列表视图显示在表单上,​​当编辑控件位于容器内时,它不能正常工作,或者在这种情况下是 datagridview。

    当编辑控件在单元格中时,this.Location 将为 ~0,0。您需要将其转换为坐标。

    https://stackoverflow.com/a/1478105/4605432

    // in the AutoCompleteTextBox itself
        private Form ParentForm
        {
            get
            {
                if (this.Parent != null)
                    return this.Parent.FindForm();
                else
                    return null;
            }
        }
    
        private void UpdateListBoxItems()
        {
            // if there is a ParentForm
            if ((ParentForm != null))
            {
        // this will get the position relative to the form, use instead of this.Location
                Point formposition = this.ParentForm.PointToClient(this.Parent.PointToScreen(this.Location));
                // get its width
                panel.Width = this.Width;
                // calculate the remeining height beneath the TextBox
                panel.Height = this.ParentForm.ClientSize.Height - this.Height - formposition.Y;
                // and the Location to use
                panel.Location = formposition + new Size(0, this.Height);
                // Panel and ListBox have to be added to ParentForm.Controls before calling BingingContext
                if (!this.ParentForm.Controls.Contains(panel))
                {
                    // add the Panel and ListBox to the PartenForm
                    this.ParentForm.Controls.Add(panel);
                }
                ((CurrencyManager)listBox.BindingContext[CurrentAutoCompleteList]).Refresh();
            }
        }
    

    但是还有其他问题超出了这个问题的范围,比如我在编辑值时有时会在 UpdateListBoxItems() 中遇到 ArgumentOutOfRange。如果控件停用,列表框并不总是隐藏自己,因此可以分离。老实说,控制似乎有点乱。

    【讨论】:

    • 没问题 Ranjan!如果此答案有助于解决您的问题,请使用复选标记将其标记为已接受。作为一个新用户,它会帮助我。 i.stack.imgur.com/QpogP.png
    猜你喜欢
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-31
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    相关资源
    最近更新 更多