2017-05-30 94 views
0

我想在使用C#的WinForms中使用DataGridView创建一个自动完成列。我设法使用DataGridView的EditingControlShowing事件来工作,并且工作正常。在DataGridView中创建自动完成列

但是,按照正常的自动完成文本框,过滤的列表仅显示基于“开始”的数据过滤。为了解决这个问题,我使用了从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); 
    } 
} 

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

回答

0

我已经完成了其中的一些功能,虽然它们非常强大,但实际上比实际情况复杂一些。

首先,您没有在列级别的任何位置传递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; 
    } 
} 

上我们仍然可以通过修改它使用电池级列表,作为替代细胞类的自动完成列表属性。

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方法创建空引用。
但是比这更基本的是listview被显示在窗体上,当编辑控件位于一个容器内,或者在这种情况下是一个datagridview时,它不能正常工作。

这个。当它在一个单元格中时,编辑控件的位置将是~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(); 
     } 
    } 

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

+0

谢谢..问题解决.. –

+0

没问题Ranjan! 如果此答案有助于解决您的问题,请将其标记为已接受并带有复选标记。这会帮助我成为新用户。 https://i.stack.imgur.com/QpogP.png –