0

I have a custom TextBox control (It is a custom one instead of a regular one to be able to have hint text on top of it), in which I have an AutoComplete that gets it's data from my DB using a DataSet like this

string[] postSource = aux.Tables[0].AsEnumerable().Select<System.Data.DataRow, String>(x => x.Field<String>("nm_industria")).ToArray();
            var source = new AutoCompleteStringCollection();
            source.AddRange(postSource);

txb_regClientData.AutoCompleteCustomSource = source;
            txb_regClientData.AutoCompleteMode = AutoCompleteMode.Suggest;
            txb_regClientData.AutoCompleteSource = AutoCompleteSource.CustomSource;

It gives me this result Image if I type "João" it will give me the correct result, but if I type "Joao" it will not show up, from some reading on the matter I know that theres nothing in the AutoComplete to automatically ignore accentuation, so I will need to code it myself. My question is, where do I begin with this? My ideal solution would be to override something in the AutoComplete code of my custom control, I gave a read on the documentation for TextBox and couldn't find anything, so if anyone can show me the right direction so I can read and learn how to do this, would be highly appreciated.

Amy
  • 9
  • 5
  • it seems that [this answer](https://stackoverflow.com/a/249126/5174469) would be a good start for you. As for the rest I might suggest to write a wrapper class which contains both representations of the strings, or even a dictionary. And do the filtering in the textchanged event? – Mong Zhu Mar 23 '21 at 12:53
  • Thats a good lead, also by making a wrapper and filtering textchanged, you mean making the whole AutoComplete myself, or something along the lines of changing the input the AutoComplete gets? – Amy Mar 23 '21 at 13:22
  • " or something along the lines of changing the input the AutoComplete gets" actually that was my first thought. Give one representation for filtering and one for display. But then I though, that this probably would not be possible and you have to go through the trouble of " making the whole AutoComplete myself" – Mong Zhu Mar 23 '21 at 14:12
  • 1
    managed to get a label that changes to the best suggestion, the entire code is inside my custom control, I think I'm gonna see if I can append a `listbox` to the `textbox` with the suggestions – Amy Mar 23 '21 at 14:53

1 Answers1

0

There's still improvements to be done, but this code implements the solution for this, it "appends" a listBox to the textBox, there will be issues when multiple of these exist due to naming, but this should be a good starting point/reference for someone looking for something similar.

public class ExTextBox : TextBox
{
    private bool alreadyRun = false;
    ListBox suggestions = new ListBox();
    int maxSize = 10;
    string[] source;
    public string Hint

    public int MaxSuggestionBoxSize
    {
        get { return maxSize; }
        set { maxSize = value; this.Invalidate(); }
    }

    protected override void OnCreateControl()
    {
        if (alreadyRun == false) // This is only supposed to be run once, but in some situations depending on the design of the main form,
        {// this might need to be somewhere that gets called multiple times, this variable makes so this code is only run once even in those situations
            suggestions.Name = "suggList_" + this.Name;
            suggestions.Location = new Point(this.Location.X, (this.Location.Y + this.Size.Height));
            suggestions.Size = new Size(this.Size.Width, this.Size.Height);
            this.Parent.Controls.Add(suggestions);
            this.Parent.Controls["suggList_" + this.Name].MouseDoubleClick += suggList_MouseDoubleClick;
            this.Parent.Controls["suggList_" + this.Name].Hide();
            alreadyRun = true;
        }
        base.OnCreateControl();
    }

    private void suggList_MouseDoubleClick(object sender, System.EventArgs e)
    {
        this.Text = this.Parent.Controls["suggList_" + this.Name].Text;
    }

    protected override void OnLeave(EventArgs e)
    {
        base.OnLeave(e);
        if(source != null) // Makes sure this code is only executed when the suggestion box is being used, by checking the existance of the source
        {
            try
            {
                if (this.Parent.Controls["suggList_" + this.Name].Focused == false)
                {
                    this.Parent.Controls["suggList_" + this.Name].Hide();
                }
            }
            catch
            {

            }
        }
    }

    protected override void OnTextChanged(EventArgs e)
    {
        base.OnTextChanged(e);
        AutoCompleteSmart();
    }

    public void AutoCompleteSmart()
    {
        if (source != null)
        {
            suggestions.Items.Clear();
            if (this.Text != "")
            {
                foreach (string a in source)
                {
                    if (RemoveDiacritics(a.ToLower()).Contains(RemoveDiacritics(this.Text.ToLower())))
                    {
                        suggestions.Items.Add(a);
                    }
                }
                this.Parent.Controls.Remove(suggestions);
                if (suggestions.Items.Count < maxSize) // Optional code, defines a limit size for the suggestion box
                {
                    suggestions.Size = new Size(this.Size.Width, ((suggestions.ItemHeight * suggestions.Items.Count) + suggestions.ItemHeight));
                }
                else
                {
                    suggestions.Size = new Size(this.Size.Width, ((suggestions.ItemHeight * maxSize) + suggestions.ItemHeight));
                }

                this.Parent.Controls.Add(suggestions);
                this.Parent.Controls["suggList_" + this.Name].BringToFront();
                this.Parent.Controls["suggList_" + this.Name].Show();
            }
            else
            {
                this.Parent.Controls["suggList_" + this.Name].Hide();
            }
        }
    }

    public void AutoCompleteSmartSource(string[] _source)
    {
        source = _source;   
    }

    private static string RemoveDiacritics(string text)
    {
        var normalizedString = text.Normalize(NormalizationForm.FormD);
        var stringBuilder = new StringBuilder();

        foreach (var c in normalizedString)
        {
            var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
            if (unicodeCategory != UnicodeCategory.NonSpacingMark)
            {
                stringBuilder.Append(c);
            }
        }

        return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
    }
}
Amy
  • 9
  • 5