2017-05-30 85 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 –

相關問題