using System.Linq; namespace System.Windows.Forms { [System.ComponentModel.DesignerCategory(@"Code")] public class SearchComboBox : ComboBox { public SearchComboBox() { AutoCompleteMode = AutoCompleteMode.Suggest; } private string prevKeyword; private string Keyword { get => _keyword; set { prevKeyword = _keyword; if (value == null) { _keyword = null; return; } if (_keyword == value) { return; } _keyword = value; ReevaluateCompletionList(); } } protected override void OnTextChanged(EventArgs e) { try { if (string.IsNullOrEmpty(Text)) { if (!IsOriginalItems) ResetCompletionList(); Keyword = null; } else { if (AutoFillTag.All(o => o.ToString() != Text)) { Keyword = Text; } } } finally { base.OnTextChanged(e); } } private object[] AutoFillTag { get { if (Tag == null) { Tag = Items.Cast().ToArray(); } return (object[]) Tag; } } private bool IsOriginalItems => Items.Count == AutoFillTag.Length; public void ResetCompletionList() { Keyword = null; try { SuspendLayout(); if (IsOriginalItems) return; Items.Clear(); Items.AddRange(AutoFillTag); } finally { ResumeLayout(true); } } private static int findFirstDifIndex(string s1, string s2) { for (var i = 0; i < Math.Min(s1.Length, s2.Length); i++) if (s1[i] != s2[i]) return i; return -1; } private object[] _newList; private string _keyword; private void ReevaluateCompletionList() { SuspendLayout(); var keyword = Keyword.ToLowerInvariant().Trim(); var selectionStart = SelectionStart; if (selectionStart == Text.Length) { selectionStart = -1; } else { selectionStart = findFirstDifIndex(prevKeyword, Keyword); } try { var originalList = AutoFillTag; if (originalList == null) { Tag = originalList = Items.Cast().ToArray(); } if (string.IsNullOrEmpty(Keyword)) { ResetCompletionList(); return; } else { _newList = originalList.Where(x => x.ToString().ToLowerInvariant().Contains(keyword)).ToArray(); } if (_newList.Any()) { Items.Clear(); Items.AddRange(_newList.ToArray()); if (!DroppedDown) { DroppedDown = true; } else { // TODO 预期下拉框高度变长则重新打开下拉框 } Cursor.Current = Cursors.Default; } else { DroppedDown = false; Items.Clear(); } if (selectionStart == -1) { Select(Text.Length, 0); } else { Select(selectionStart + 1, 0); } } finally { ResumeLayout(true); } } } }