4 using System.Collections;
6 using System.Threading.Tasks;
8 using System.Windows.Controls;
9 using System.Windows.Controls.Primitives;
10 using System.Windows.Data;
11 using System.Windows.Input;
13 using SiliconStudio.Presentation.Core;
14 using SiliconStudio.Presentation.Extensions;
16 namespace SiliconStudio.Presentation.Controls
18 [TemplatePart(Name =
"PART_EditableTextBox", Type = typeof(TextBox))]
19 [TemplatePart(Name =
"PART_ListBox", Type = typeof(ListBox))]
25 private string tokenLowercase;
27 public string Token {
get {
return token; } set { token = value; tokenLowercase = (value ??
"").ToLowerInvariant(); } }
34 if (
string.IsNullOrWhiteSpace(token))
35 return string.Compare(a, b, StringComparison.InvariantCultureIgnoreCase);
37 var indexA = a.IndexOf(tokenLowercase, StringComparison.InvariantCultureIgnoreCase);
38 var indexB = b.IndexOf(tokenLowercase, StringComparison.InvariantCultureIgnoreCase);
40 if (indexA == 0 && indexB > 0)
42 if (indexB == 0 && indexA > 0)
45 return string.Compare(
a,
b, StringComparison.InvariantCultureIgnoreCase);
52 private readonly FilteringComboBoxSort sort;
57 private TextBox editableTextBox;
62 private ListBox listBox;
67 private bool clearing;
71 private bool updatingSelection;
76 private bool validating;
78 public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
"IsDropDownOpen", typeof(
bool), typeof(
FilteringComboBox));
80 public static readonly DependencyProperty ClearTextAfterValidationProperty = DependencyProperty.Register(
"ClearTextAfterValidation", typeof(
bool), typeof(
FilteringComboBox));
85 public static readonly DependencyProperty WatermarkContentProperty = DependencyProperty.Register(
"WatermarkContent", typeof(
object), typeof(
FilteringComboBox),
new PropertyMetadata(null));
87 public static readonly DependencyProperty ItemsToExcludeProperty = DependencyProperty.Register(
"ItemsToExclude", typeof(
IEnumerable), typeof(
FilteringComboBox));
97 public static readonly RoutedEvent ValidatedEvent = EventManager.RegisterRoutedEvent(
"Validated", RoutingStrategy.Bubble, typeof(ValidationRoutedEventHandler<string>), typeof(
FilteringComboBox));
102 IsTextSearchEnabled =
false;
105 public bool IsDropDownOpen {
get {
return (
bool)GetValue(IsDropDownOpenProperty); } set { SetValue(IsDropDownOpenProperty, value); } }
107 public bool ClearTextAfterValidation {
get {
return (
bool)GetValue(ClearTextAfterValidationProperty); } set { SetValue(ClearTextAfterValidationProperty, value); } }
112 public object WatermarkContent {
get {
return GetValue(WatermarkContentProperty); } set { SetValue(WatermarkContentProperty, value); } }
114 public IEnumerable ItemsToExclude {
get {
return (
IEnumerable)GetValue(ItemsToExcludeProperty); } set { SetValue(ItemsToExcludeProperty, value); } }
119 public event CancelRoutedEventHandler Validating { add { AddHandler(ValidatingEvent, value); }
remove { RemoveHandler(ValidatingEvent, value); } }
124 public event ValidationRoutedEventHandler<string> Validated { add { AddHandler(ValidatedEvent, value); }
remove { RemoveHandler(ValidatedEvent, value); } }
128 base.OnItemsSourceChanged(oldValue, newValue);
129 if (newValue != null)
131 var cvs = (CollectionView)CollectionViewSource.GetDefaultView(newValue);
133 var listCollectionView = cvs as ListCollectionView;
134 if (listCollectionView != null)
136 listCollectionView.CustomSort = sort;
143 base.OnApplyTemplate();
145 editableTextBox = GetTemplateChild(
"PART_EditableTextBox") as
TextBox;
146 if (editableTextBox == null)
147 throw new InvalidOperationException(
"A part named 'PART_EditableTextBox' must be present in the ControlTemplate, and must be of type 'SiliconStudio.Presentation.Controls.Input.TextBox'.");
149 listBox = GetTemplateChild(
"PART_ListBox") as ListBox;
151 throw new InvalidOperationException(
"A part named 'PART_ListBox' must be present in the ControlTemplate, and must be of type 'ListBox'.");
153 editableTextBox.TextChanged += EditableTextBoxTextChanged;
154 editableTextBox.PreviewKeyDown += EditableTextBoxPreviewKeyDown;
155 editableTextBox.Validating += EditableTextBoxValidating;
156 editableTextBox.Validated += EditableTextBoxValidated;
157 editableTextBox.Cancelled += EditableTextBoxCancelled;
158 editableTextBox.LostFocus += EditableTextBoxLostFocus;
159 listBox.PreviewMouseUp += ListBoxMouseUp;
164 base.OnSelectionChanged(e);
165 if (SelectedItem == null && !updatingSelection)
168 editableTextBox.Clear();
173 private void UpdateText()
175 if (listBox.SelectedItem != null)
177 editableTextBox.Text = listBox.SelectedItem.ToString();
178 IsDropDownOpen =
false;
185 if (!ReferenceEquals(sender, editableTextBox))
193 RaiseEvent(cancelRoutedEventArgs);
194 if (cancelRoutedEventArgs.Cancel)
201 if (!ReferenceEquals(sender, editableTextBox))
205 RaiseEvent(validatedArgs);
207 if (ClearTextAfterValidation)
210 editableTextBox.Text = string.Empty;
215 private async
void EditableTextBoxCancelled(
object sender,
RoutedEventArgs e)
218 if (!ReferenceEquals(sender, editableTextBox))
222 editableTextBox.Text = string.Empty;
225 await Task.Delay(100);
226 IsDropDownOpen =
false;
230 private async
void EditableTextBoxLostFocus(
object sender,
RoutedEventArgs e)
233 if (!ReferenceEquals(sender, editableTextBox))
239 await Task.Delay(100);
240 IsDropDownOpen =
false;
244 private void ListBoxMouseUp(
object sender, MouseButtonEventArgs e)
246 if (e.ChangedButton ==
MouseButton.Left && listBox.SelectedIndex > -1)
249 editableTextBox.Validate();
253 private void EditableTextBoxTextChanged(
object sender, TextChangedEventArgs e)
255 if (ItemsSource == null)
258 updatingSelection =
true;
259 if (!IsDropDownOpen && !clearing)
262 var index = editableTextBox.CaretIndex;
263 IsDropDownOpen =
true;
264 editableTextBox.CaretIndex = index;
266 sort.Token = editableTextBox.Text;
267 var cvs = CollectionViewSource.GetDefaultView(ItemsSource);
269 if (listBox.Items.Count > 0 && !validating)
271 listBox.SelectedIndex = 0;
273 updatingSelection =
false;
276 private void EditableTextBoxPreviewKeyDown(
object sender, KeyEventArgs e)
278 if (listBox.Items.Count > 0)
280 updatingSelection =
true;
281 if (e.Key == Key.Escape)
283 IsDropDownOpen =
false;
287 listBox.SelectedIndex = Math.Max(listBox.SelectedIndex - 1, 0);
288 if (listBox.SelectedItem != null)
289 listBox.ScrollIntoView(listBox.SelectedItem);
291 if (e.Key == Key.Down)
293 listBox.SelectedIndex = Math.Min(listBox.SelectedIndex + 1, listBox.Items.Count - 1);
294 if (listBox.SelectedItem != null)
295 listBox.ScrollIntoView(listBox.SelectedItem);
297 if (e.Key == Key.PageUp)
299 var stackPanel = listBox.FindVisualChildOfType<VirtualizingStackPanel>();
300 if (stackPanel != null)
302 var
count = stackPanel.Children.Count;
303 listBox.SelectedIndex = Math.Max(listBox.SelectedIndex -
count, 0);
307 listBox.SelectedIndex = 0;
309 if (listBox.SelectedItem != null)
310 listBox.ScrollIntoView(listBox.SelectedItem);
312 if (e.Key == Key.PageDown)
314 var stackPanel = listBox.FindVisualChildOfType<VirtualizingStackPanel>();
315 if (stackPanel != null)
317 var
count = stackPanel.Children.Count;
318 listBox.SelectedIndex = Math.Min(listBox.SelectedIndex +
count, listBox.Items.Count - 1);
322 listBox.SelectedIndex = listBox.Items.Count - 1;
324 if (listBox.SelectedItem != null)
325 listBox.ScrollIntoView(listBox.SelectedItem);
327 if (e.Key == Key.Home)
329 listBox.SelectedIndex = 0;
331 if (e.Key == Key.End)
333 listBox.SelectedIndex = listBox.Items.Count - 1;
335 updatingSelection =
false;
339 private bool Filter(
object obj)
341 if (editableTextBox == null)
344 var filter = editableTextBox.Text;
345 if (
string.IsNullOrWhiteSpace(filter))
351 if (ItemsToExclude != null && ItemsToExclude.Cast<
object>().Contains(obj))
354 var text = obj.ToString();
355 return text.IndexOf(filter, StringComparison.InvariantCultureIgnoreCase) > -1 || MatchCamelCase(text);
358 private bool MatchCamelCase(
string text)
360 var camelCaseSplit = text.CamelCaseSplit();
361 var filter = editableTextBox.Text.ToLowerInvariant();
362 int currentFilterChar = 0;
364 foreach (var word
in camelCaseSplit)
366 int currentWordChar = 0;
367 while (currentFilterChar > 0)
369 if (
char.ToLower(word[currentWordChar]) == filter[currentFilterChar])
374 while (
char.ToLower(word[currentWordChar]) == filter[currentFilterChar])
378 if (currentFilterChar == filter.Length)
381 if (currentWordChar == word.Length)
385 return currentFilterChar == filter.Length;
override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
An implementation of the TextBoxBase control that provides additional features such as a proper valid...
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
override void OnApplyTemplate()
int Compare(object x, object y)
override void OnSelectionChanged(SelectionChangedEventArgs e)
delegate void CancelRoutedEventHandler(object sender, CancelRoutedEventArgs e)