2009-09-17 6 views
24

Ich habe eine Textbox, die die automatische Vervollständigung in etwa so:WinForms | C# | AutoComplete in der Mitte einer Textbox?

txtName.AutoCompleteMode = AutoCompleteMode.Suggest; 
txtName.AutoCompleteSource = AutoCompleteSource.CustomSource; 
txtName.AutoCompleteCustomSource = namesCollection; 

Es funktioniert, aber nur am Anfang einer Textbox. Ich möchte, dass die automatische Vervollständigung für jedes Wort, das der Benutzer eingibt, an einer beliebigen Position in der Textbox aktiviert wird.

+1

dann müssen Sie diese Funktionalität –

+0

ah ok, also nichts gebacken schreiben ... nur OnTextChanged verwenden und meine eigenen ... Dank schreiben. – Chaddeus

+0

Kennen Sie gute Artikel zum Schreiben einer benutzerdefinierten Autovervollständigung in C# für WinForms? – Chaddeus

Antwort

32
using System; 
using System.Collections.Generic; 
using System.Drawing; 
using System.Windows.Forms; 

namespace TubeUploader 
{ 
    public class AutoCompleteTextBox : TextBox 
    { 
     private ListBox _listBox; 
     private bool _isAdded; 
     private String[] _values; 
     private String _formerValue = String.Empty; 

     public AutoCompleteTextBox() 
     { 
      InitializeComponent(); 
      ResetListBox(); 
     } 

     private void InitializeComponent() 
     { 
      _listBox = new ListBox(); 
      KeyDown += this_KeyDown; 
      KeyUp += this_KeyUp; 
     } 

     private void ShowListBox() 
     { 
      if (!_isAdded) 
      { 
       Parent.Controls.Add(_listBox); 
       _listBox.Left = Left; 
       _listBox.Top = Top + Height; 
       _isAdded = true; 
      } 
      _listBox.Visible = true; 
      _listBox.BringToFront(); 
     } 

     private void ResetListBox() 
     { 
      _listBox.Visible = false; 
     } 

     private void this_KeyUp(object sender, KeyEventArgs e) 
     { 
      UpdateListBox(); 
     } 

     private void this_KeyDown(object sender, KeyEventArgs e) 
     { 
      switch (e.KeyCode) 
      { 
       case Keys.Tab: 
        { 
         if (_listBox.Visible) 
         { 
          InsertWord((String)_listBox.SelectedItem); 
          ResetListBox(); 
          _formerValue = Text; 
         } 
         break; 
        } 
       case Keys.Down: 
        { 
         if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1)) 
          _listBox.SelectedIndex++; 

         break; 
        } 
       case Keys.Up: 
        { 
         if ((_listBox.Visible) && (_listBox.SelectedIndex > 0)) 
          _listBox.SelectedIndex--; 

         break; 
        } 
      } 
     } 

     protected override bool IsInputKey(Keys keyData) 
     { 
      switch (keyData) 
      { 
       case Keys.Tab: 
        return true; 
       default: 
        return base.IsInputKey(keyData); 
      } 
     } 

     private void UpdateListBox() 
     { 
      if (Text == _formerValue) return; 
      _formerValue = Text; 
      String word = GetWord(); 

      if (_values != null && word.Length > 0) 
      { 
       String[] matches = Array.FindAll(_values, 
               x => (x.StartsWith(word, StringComparison.OrdinalIgnoreCase) && !SelectedValues.Contains(x))); 
       if (matches.Length > 0) 
       { 
        ShowListBox(); 
        _listBox.Items.Clear(); 
        Array.ForEach(matches, x => _listBox.Items.Add(x)); 
        _listBox.SelectedIndex = 0; 
        _listBox.Height = 0; 
        _listBox.Width = 0; 
        Focus(); 
        using (Graphics graphics = _listBox.CreateGraphics()) 
        { 
         for (int i = 0; i < _listBox.Items.Count; i++) 
         { 
          _listBox.Height += _listBox.GetItemHeight(i); 
          // it item width is larger than the current one 
          // set it to the new max item width 
          // GetItemRectangle does not work for me 
          // we add a little extra space by using '_' 
          int itemWidth = (int)graphics.MeasureString(((String)_listBox.Items[i]) + "_", _listBox.Font).Width; 
          _listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : _listBox.Width; 
         } 
        } 
       } 
       else 
       { 
        ResetListBox(); 
       } 
      } 
      else 
      { 
       ResetListBox(); 
      } 
     } 

     private String GetWord() 
     { 
      String text = Text; 
      int pos = SelectionStart; 

      int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1); 
      posStart = (posStart == -1) ? 0 : posStart + 1; 
      int posEnd = text.IndexOf(' ', pos); 
      posEnd = (posEnd == -1) ? text.Length : posEnd; 

      int length = ((posEnd - posStart) < 0) ? 0 : posEnd - posStart; 

      return text.Substring(posStart, length); 
     } 

     private void InsertWord(String newTag) 
     { 
      String text = Text; 
      int pos = SelectionStart; 

      int posStart = text.LastIndexOf(' ', (pos < 1) ? 0 : pos - 1); 
      posStart = (posStart == -1) ? 0 : posStart + 1; 
      int posEnd = text.IndexOf(' ', pos); 

      String firstPart = text.Substring(0, posStart) + newTag; 
      String updatedText = firstPart + ((posEnd == -1) ? "" : text.Substring(posEnd, text.Length - posEnd)); 


      Text = updatedText; 
      SelectionStart = firstPart.Length; 
     } 

     public String[] Values 
     { 
      get 
      { 
       return _values; 
      } 
      set 
      { 
       _values = value; 
      } 
     } 

     public List<String> SelectedValues 
     { 
      get 
      { 
       String[] result = Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 
       return new List<String>(result); 
      } 
     } 

    } 

} 

Probe Nutzungs

using System; 
using System.Windows.Forms; 

namespace AutoComplete 
{ 
    public partial class TestForm : Form 
    { 
     private readonly String[] _values = { "one", "two", "three", "tree", "four", "fivee" }; 

     public TestForm() 
     { 
      InitializeComponent(); 
      // AutoComplete is our special textbox control on the form 
      AutoComplete.Values = _values; 
     } 

    } 
} 
+2

Dies hat den zusätzlichen Vorteil, gut mit mehrzeiligen Textfeldern zu arbeiten. (Achten Sie jedoch darauf, 'AcceptsTab' auf * true * zu setzen.) Unglaublich nützlich! – ladenedge

+0

Sie verdienen wirklich +1, und ich würde Ihnen mehr geben, wenn ich könnte. Ich verwende jetzt Ihre AutoCompleteTextBox in meinem Open-Source-Projekt und es ist eine große Verbesserung für meine Benutzererfahrung. Vielen Dank! – teamalpha5441

+7

Der Besitzer dieses benutzerdefinierten Steuercodes ist Peter Holpar, veröffentlicht im Jahr 2010: http://pholpar.wordpress.com/2010/02/25/multivalue-autocomplete-winforms-textbox-for-tagging/ Der Quellcode kann heruntergeladen werden at: http://autocompletetexboxcs.codeplex.com/ Das nächste Mal bestätigen jemand Beitrag und Arbeit, wenn Sie über die Programmierung kümmern. Vergessen Sie nicht, Kredit zu geben, auch wenn es ein freier offener Code ist, es ist kein Plagiat, aber es ist unhöflich und unhöflich. – WhySoSerious

7

machte ich ein paar Änderungen an der Lösung von @PaRiMaL RaJ vorgeschlagen, da das Listenfeld nicht angezeigt wird, wurde, wenn das Textfeld in einem Usercontrol war, die nicht groß genug war, . Anstatt das Listenfeld zum übergeordneten Element des Textfelds hinzuzufügen, habe ich das Formular hinzugefügt und die absolute Position im Formular berechnet.

public class AutoCompleteTextBox : TextBox 
    { 
     private ListBox _listBox; 
     private bool _isAdded; 
     private String[] _values; 
     private String _formerValue = String.Empty; 

     public AutoCompleteTextBox() 
     { 
      InitializeComponent(); 
      ResetListBox(); 
     } 

     private void InitializeComponent() 
     { 
      _listBox = new ListBox(); 
      this.KeyDown += this_KeyDown; 
      this.KeyUp += this_KeyUp; 
     } 

     private void ShowListBox() 
     { 
      if (!_isAdded) 
      { 
       Form parentForm = this.FindForm(); // new line added 
       parentForm.Controls.Add(_listBox); // adds it to the form 
       Point positionOnForm = parentForm.PointToClient(this.Parent.PointToScreen(this.Location)); // absolute position in the form 
       _listBox.Left = positionOnForm.X; 
       _listBox.Top = positionOnForm.Y + Height; 
       _isAdded = true; 
      } 
      _listBox.Visible = true; 
      _listBox.BringToFront(); 
     } 



     private void ResetListBox() 
     { 
      _listBox.Visible = false; 
     } 

     private void this_KeyUp(object sender, KeyEventArgs e) 
     { 
      UpdateListBox(); 
     } 

     private void this_KeyDown(object sender, KeyEventArgs e) 
     { 
      switch (e.KeyCode) 
      { 
       case Keys.Enter: 
       case Keys.Tab: 
        { 
         if (_listBox.Visible) 
         { 
          Text = _listBox.SelectedItem.ToString(); 
          ResetListBox(); 
          _formerValue = Text; 
          this.Select(this.Text.Length, 0); 
          e.Handled = true; 
         } 
         break; 
        } 
       case Keys.Down: 
        { 
         if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1)) 
          _listBox.SelectedIndex++; 
         e.Handled = true; 
         break; 
        } 
       case Keys.Up: 
        { 
         if ((_listBox.Visible) && (_listBox.SelectedIndex > 0)) 
          _listBox.SelectedIndex--; 
         e.Handled = true; 
         break; 
        } 


      } 
     } 

     protected override bool IsInputKey(Keys keyData) 
     { 
      switch (keyData) 
      { 
       case Keys.Tab: 
        if (_listBox.Visible) 
         return true; 
        else 
         return false; 
       default: 
        return base.IsInputKey(keyData); 
      } 
     } 

     private void UpdateListBox() 
     { 
      if (Text == _formerValue) 
       return; 

      _formerValue = this.Text; 
      string word = this.Text; 

      if (_values != null && word.Length > 0) 
      { 
       string[] matches = Array.FindAll(_values, 
               x => (x.ToLower().Contains(word.ToLower()))); 
       if (matches.Length > 0) 
       { 
        ShowListBox(); 
        _listBox.BeginUpdate(); 
        _listBox.Items.Clear(); 
        Array.ForEach(matches, x => _listBox.Items.Add(x)); 
        _listBox.SelectedIndex = 0; 
        _listBox.Height = 0; 
        _listBox.Width = 0; 
        Focus(); 
        using (Graphics graphics = _listBox.CreateGraphics()) 
        { 
         for (int i = 0; i < _listBox.Items.Count; i++) 
         { 
          if (i < 20) 
           _listBox.Height += _listBox.GetItemHeight(i); 
          // it item width is larger than the current one 
          // set it to the new max item width 
          // GetItemRectangle does not work for me 
          // we add a little extra space by using '_' 
          int itemWidth = (int)graphics.MeasureString(((string)_listBox.Items[i]) + "_", _listBox.Font).Width; 
          _listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : this.Width; ; 
         } 
        } 
        _listBox.EndUpdate(); 
       } 
       else 
       { 
        ResetListBox(); 
       } 
      } 
      else 
      { 
       ResetListBox(); 
      } 
     } 

     public String[] Values 
     { 
      get 
      { 
       return _values; 
      } 
      set 
      { 
       _values = value; 
      } 
     } 

     public List<String> SelectedValues 
     { 
      get 
      { 
       String[] result = Text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); 
       return new List<String>(result); 
      } 
     } 

    } 
0

Die anderen Lösungen auf meine Bedürfnisse nicht in einem mehrzeiligen Umgebung für mich arbeiten, also habe ich zu @Francisco Golden Antwort hinzugefügt, dies zu ermöglichen. Was ich brauchte, war, jedes 'Wort' in der TextBox und in jeder Position/Zeile automatisch zu vervollständigen. Nach minimalen Tests scheint diese Klasse gut genug für mich in einer mehrzeiligen TextBox zu funktionieren. Hoffe es hilft jemandem.

Hauptänderungen sind in UpdateListBox() und this_KeyDown(), um sich mit dem "aktuellen" Wort, d. H. Dem unmittelbar vor der Caret-Position, statt mit dem gesamten Textboxinhalt zu befassen.

Ändern Sie die Definition von separators in UpdateListBox(), um Ihre Anforderungen zu erfüllen.

using System; 
using System.Drawing; 
using System.Windows.Forms; 

class MultiLineAutoCompleteTextBox : TextBox 
{ 
    private ListBox _listBox; 
    private bool _isAdded; 
    private String[] _values; 
    private String _formerValue = String.Empty; 
    private int _prevBreak; 
    private int _nextBreak; 
    private int _wordLen; 

    public MultiLineAutoCompleteTextBox() 
    { 
     InitializeComponent(); 
     ResetListBox(); 
    } 

    private void InitializeComponent() 
    { 
     _listBox = new ListBox(); 
     KeyDown += this_KeyDown; 
     KeyUp += this_KeyUp; 
    } 

    private void ShowListBox() 
    { 
     if (!_isAdded) 
     { 
      Form parentForm = FindForm(); 
      if (parentForm == null) return; 

      parentForm.Controls.Add(_listBox); 
      Point positionOnForm = parentForm.PointToClient(Parent.PointToScreen(Location)); 
      _listBox.Left = positionOnForm.X; 
      _listBox.Top = positionOnForm.Y + Height; 
      _isAdded = true; 
     } 
     _listBox.Visible = true; 
     _listBox.BringToFront(); 
    } 

    private void ResetListBox() 
    { 
     _listBox.Visible = false; 
    } 

    private void this_KeyUp(object sender, KeyEventArgs e) 
    { 
     UpdateListBox(); 
    } 

    private void this_KeyDown(object sender, KeyEventArgs e) 
    { 
     switch (e.KeyCode) 
     { 
      case Keys.Enter: 
      case Keys.Tab: 
      case Keys.Space: 
      { 
       if (_listBox.Visible) 
       { 
        Text = Text.Remove(_prevBreak == 0 ? 0 : _prevBreak + 1, _prevBreak == 0 ? _wordLen + 1 : _wordLen); 
        Text = Text.Insert(_prevBreak == 0 ? 0 : _prevBreak + 1, _listBox.SelectedItem.ToString()); 
        ResetListBox(); 
        _formerValue = Text; 
        Select(Text.Length, 0); 
        e.Handled = true; 
       } 
       break; 
      } 
      case Keys.Down: 
      { 
       if ((_listBox.Visible) && (_listBox.SelectedIndex < _listBox.Items.Count - 1)) 
        _listBox.SelectedIndex++; 
       e.Handled = true; 
       break; 
      } 
      case Keys.Up: 
      { 
       if ((_listBox.Visible) && (_listBox.SelectedIndex > 0)) 
        _listBox.SelectedIndex--; 
       e.Handled = true; 
       break; 
      } 


     } 
    } 

    protected override bool IsInputKey(Keys keyData) 
    { 
     switch (keyData) 
     { 
      case Keys.Tab: 
       if (_listBox.Visible) 
        return true; 
       else 
        return false; 
      default: 
       return base.IsInputKey(keyData); 
     } 
    } 

    private void UpdateListBox() 
    { 
     if (Text == _formerValue) return; 
     if (Text.Length == 0) 
     { 
      _listBox.Visible = false; 
      return; 
     } 

     _formerValue = Text; 
     var separators = new[] { '|', '[', ']', '\r', '\n', ' ', '\t' }; 
     _prevBreak = Text.LastIndexOfAny(separators, CaretIndex > 0 ? CaretIndex - 1 : 0); 
     if (_prevBreak < 1) _prevBreak = 0; 
     _nextBreak = Text.IndexOfAny(separators, _prevBreak + 1); 
     if (_nextBreak == -1) _nextBreak = CaretIndex; 
     _wordLen = _nextBreak - _prevBreak - 1; 
     if (_wordLen < 1) return; 

     string word = Text.Substring(_prevBreak + 1, _wordLen); 

     if (_values != null && word.Length > 0) 
     { 
      string[] matches = Array.FindAll(_values, 
       x => (x.ToLower().Contains(word.ToLower()))); 
      if (matches.Length > 0) 
      { 
       ShowListBox(); 
       _listBox.BeginUpdate(); 
       _listBox.Items.Clear(); 
       Array.ForEach(matches, x => _listBox.Items.Add(x)); 
       _listBox.SelectedIndex = 0; 
       _listBox.Height = 0; 
       _listBox.Width = 0; 
       Focus(); 
       using (Graphics graphics = _listBox.CreateGraphics()) 
       { 
        for (int i = 0; i < _listBox.Items.Count; i++) 
        { 
         if (i < 20) 
          _listBox.Height += _listBox.GetItemHeight(i); 
         // it item width is larger than the current one 
         // set it to the new max item width 
         // GetItemRectangle does not work for me 
         // we add a little extra space by using '_' 
         int itemWidth = (int)graphics.MeasureString(((string)_listBox.Items[i]) + "_", _listBox.Font).Width; 
         _listBox.Width = (_listBox.Width < itemWidth) ? itemWidth : Width; ; 
        } 
       } 
       _listBox.EndUpdate(); 
      } 
      else 
      { 
       ResetListBox(); 
      } 
     } 
     else 
     { 
      ResetListBox(); 
     } 
    } 

    public int CaretIndex => SelectionStart; 

    public String[] Values 
    { 
     get 
     { 
      return _values; 
     } 
     set 
     { 
      _values = value; 
     } 
    } 
} 
Verwandte Themen