AutoCompleteBoxPage.xaml.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Avalonia.Controls;
  7. using Avalonia.Data;
  8. using Avalonia.Data.Converters;
  9. using Avalonia.LogicalTree;
  10. namespace ControlCatalog.Pages
  11. {
  12. public partial class AutoCompleteBoxPage : UserControl
  13. {
  14. public class StateData
  15. {
  16. public string Name { get; private set; }
  17. public string Abbreviation { get; private set; }
  18. public string Capital { get; private set; }
  19. public StateData(string name, string abbreviatoin, string capital)
  20. {
  21. Name = name;
  22. Abbreviation = abbreviatoin;
  23. Capital = capital;
  24. }
  25. public override string ToString()
  26. {
  27. return Name;
  28. }
  29. }
  30. private static StateData[] BuildAllStates()
  31. {
  32. return new StateData[]
  33. {
  34. new StateData("Alabama","AL","Montgomery"),
  35. new StateData("Alaska","AK","Juneau"),
  36. new StateData("Arizona","AZ","Phoenix"),
  37. new StateData("Arkansas","AR","Little Rock"),
  38. new StateData("California","CA","Sacramento"),
  39. new StateData("Colorado","CO","Denver"),
  40. new StateData("Connecticut","CT","Hartford"),
  41. new StateData("Delaware","DE","Dover"),
  42. new StateData("Florida","FL","Tallahassee"),
  43. new StateData("Georgia","GA","Atlanta"),
  44. new StateData("Hawaii","HI","Honolulu"),
  45. new StateData("Idaho","ID","Boise"),
  46. new StateData("Illinois","IL","Springfield"),
  47. new StateData("Indiana","IN","Indianapolis"),
  48. new StateData("Iowa","IA","Des Moines"),
  49. new StateData("Kansas","KS","Topeka"),
  50. new StateData("Kentucky","KY","Frankfort"),
  51. new StateData("Louisiana","LA","Baton Rouge"),
  52. new StateData("Maine","ME","Augusta"),
  53. new StateData("Maryland","MD","Annapolis"),
  54. new StateData("Massachusetts","MA","Boston"),
  55. new StateData("Michigan","MI","Lansing"),
  56. new StateData("Minnesota","MN","St. Paul"),
  57. new StateData("Mississippi","MS","Jackson"),
  58. new StateData("Missouri","MO","Jefferson City"),
  59. new StateData("Montana","MT","Helena"),
  60. new StateData("Nebraska","NE","Lincoln"),
  61. new StateData("Nevada","NV","Carson City"),
  62. new StateData("New Hampshire","NH","Concord"),
  63. new StateData("New Jersey","NJ","Trenton"),
  64. new StateData("New Mexico","NM","Santa Fe"),
  65. new StateData("New York","NY","Albany"),
  66. new StateData("North Carolina","NC","Raleigh"),
  67. new StateData("North Dakota","ND","Bismarck"),
  68. new StateData("Ohio","OH","Columbus"),
  69. new StateData("Oklahoma","OK","Oklahoma City"),
  70. new StateData("Oregon","OR","Salem"),
  71. new StateData("Pennsylvania","PA","Harrisburg"),
  72. new StateData("Rhode Island","RI","Providence"),
  73. new StateData("South Carolina","SC","Columbia"),
  74. new StateData("South Dakota","SD","Pierre"),
  75. new StateData("Tennessee","TN","Nashville"),
  76. new StateData("Texas","TX","Austin"),
  77. new StateData("Utah","UT","Salt Lake City"),
  78. new StateData("Vermont","VT","Montpelier"),
  79. new StateData("Virginia","VA","Richmond"),
  80. new StateData("Washington","WA","Olympia"),
  81. new StateData("West Virginia","WV","Charleston"),
  82. new StateData("Wisconsin","WI","Madison"),
  83. new StateData("Wyoming","WY","Cheyenne"),
  84. };
  85. }
  86. public StateData[] States { get; private set; }
  87. private static LinkedList<string>[] BuildAllSentences()
  88. {
  89. return new string[]
  90. {
  91. "Hello world",
  92. "No this is Patrick",
  93. "Never gonna give you up",
  94. "How does one patch KDE2 under FreeBSD"
  95. }
  96. .Select(x => new LinkedList<string>(x.Split(' ')))
  97. .ToArray();
  98. }
  99. public LinkedList<string>[] Sentences { get; private set; }
  100. public AutoCompleteBoxPage()
  101. {
  102. InitializeComponent();
  103. States = BuildAllStates();
  104. Sentences = BuildAllSentences();
  105. foreach (AutoCompleteBox box in GetAllAutoCompleteBox().Where(x => x.Name != "CustomAutocompleteBox"))
  106. {
  107. box.ItemsSource = States;
  108. }
  109. var converter = new FuncMultiValueConverter<string, string>(parts =>
  110. {
  111. return String.Format("{0} ({1})", parts.ToArray());
  112. });
  113. var binding = new MultiBinding { Converter = converter };
  114. binding.Bindings.Add(new Binding("Name"));
  115. binding.Bindings.Add(new Binding("Abbreviation"));
  116. MultiBindingBox.ValueMemberBinding = binding;
  117. AsyncBox.AsyncPopulator = PopulateAsync;
  118. CustomAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
  119. CustomAutocompleteBox.TextFilter = LastWordContains;
  120. CustomAutocompleteBox.TextSelector = AppendWord;
  121. }
  122. private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox()
  123. {
  124. return
  125. this.GetLogicalDescendants()
  126. .OfType<AutoCompleteBox>();
  127. }
  128. private static bool StringContains(string str, string? query)
  129. {
  130. if (query == null)
  131. return false;
  132. return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
  133. }
  134. private async Task<IEnumerable<object>> PopulateAsync(string? searchText, CancellationToken cancellationToken)
  135. {
  136. await Task.Delay(TimeSpan.FromSeconds(1.5), cancellationToken);
  137. return
  138. States.Where(data => StringContains(data.Name, searchText) || StringContains(data.Capital, searchText))
  139. .ToList();
  140. }
  141. private bool LastWordContains(string? searchText, string? item)
  142. {
  143. var words = searchText?.Split(' ') ?? Array.Empty<string>();
  144. var options = Sentences.Select(x => x.First)
  145. .ToArray<LinkedListNode<string>?>();
  146. for (var i = 0; i < words.Length; ++i)
  147. {
  148. var word = words[i];
  149. for (var j = 0; word is { } && j < options.Length; ++j)
  150. {
  151. if (options[i] is { } option)
  152. {
  153. if (i == words.Length - 1)
  154. {
  155. options[j] = option.Value.ToLower().Contains(word.ToLower()) ? option : null;
  156. }
  157. else
  158. {
  159. options[j] = option.Value.Equals(word, StringComparison.InvariantCultureIgnoreCase) ? option.Next : null;
  160. }
  161. }
  162. }
  163. }
  164. return options.Any(x => x != null && x.Value == item);
  165. }
  166. private string AppendWord(string? text, string? item)
  167. {
  168. if (item is { })
  169. {
  170. string[] parts = text?.Split(' ') ?? Array.Empty<string>();
  171. if (parts.Length == 0)
  172. return item;
  173. parts[parts.Length - 1] = item;
  174. return string.Join(" ", parts);
  175. }
  176. return string.Empty;
  177. }
  178. }
  179. }