WinForms | C# | saisie semi-automatique dans le Milieu d'une zone de texte?

j'ai une zone de texte qui ne l'autocomplétion comme suit:

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

Cela fonctionne, mais seulement au début d'une zone de texte. J'aimerais que autocomplete donne un coup de pied pour n'importe quel mot que l'utilisateur entre, à n'importe quelle position dans la zone de texte.

25
demandé sur Chaddeus 2009-09-17 10:21:02
la source

3 ответов

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

    }

}

Exemple D'Utilisation

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

    }
}
33
répondu PaRiMaL RaJ 2012-12-15 16:35:16
la source

j'ai apporté quelques modifications à la solution proposée par @PaRiMaL RaJ parce que la boîte de liste n'était pas affichée lorsque la boîte de texte était à l'intérieur d'un UserControl qui n'était pas assez grand. Fondamentalement, au lieu d'ajouter la boîte de liste au parent de la boîte de texte, j'ai ajouté au formulaire et je calcule la position absolue dans le formulaire.

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

    }
7
répondu Francisco Goldenstein 2015-01-19 00:33:59
la source

les autres solutions n'ont pas fonctionné pour moi dans un environnement multiligne à mes besoins, donc j'ai ajouté à la réponse de @Francisco Goldenstein pour permettre cela. Ce dont j'avais besoin était d'autocompléter n'importe quel " mot " dans la boîte de texte et dans n'importe quelle position/ligne. Après un minimum de tests, cette classe semble fonctionner assez bien pour moi dans une boîte de texte multiligne. Espérons que cela aide quelqu'un.

les principaux changements sont en UpdateListBox() et this_KeyDown(), pour faire face avec le mot, c'est à dire celui juste avant la position du curseur, plutôt que l'ensemble du contenu de la boîte de texte.

Modifier la définition de separatorsUpdateListBox() pour répondre à vos besoins.

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;
        }
    }
}
0
répondu codah 2018-03-01 02:34:51
la source

Autres questions sur