|
|
|
@ -1,4 +1,4 @@ |
|
|
|
// (c) Copyright Microsoft Corporation.
|
|
|
|
// (c) Copyright Microsoft Corporation.
|
|
|
|
// This source is subject to the Microsoft Public License (Ms-PL).
|
|
|
|
// Please see https://go.microsoft.com/fwlink/?LinkID=131993 for details.
|
|
|
|
// All other rights reserved.
|
|
|
|
@ -165,6 +165,16 @@ namespace Avalonia.Controls |
|
|
|
/// </summary>
|
|
|
|
private bool _allowWrite; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A boolean indicating if a cancellation was requested
|
|
|
|
/// </summary>
|
|
|
|
private bool _cancelRequested; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// A boolean indicating if filtering is in action
|
|
|
|
/// </summary>
|
|
|
|
private bool _filterInAction; |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// The TextBox template part.
|
|
|
|
/// </summary>
|
|
|
|
@ -1377,8 +1387,17 @@ namespace Avalonia.Controls |
|
|
|
/// Walks through the items enumeration. Performance is not going to be
|
|
|
|
/// perfect with the current implementation.
|
|
|
|
/// </summary>
|
|
|
|
private void RefreshView() |
|
|
|
private async void RefreshView() |
|
|
|
{ |
|
|
|
// If we have a running filter, trigger a request first
|
|
|
|
if (_filterInAction) |
|
|
|
{ |
|
|
|
_cancelRequested = true; |
|
|
|
} |
|
|
|
|
|
|
|
// Indicate that filtering is ongoing
|
|
|
|
_filterInAction = true; |
|
|
|
|
|
|
|
if (_items == null) |
|
|
|
{ |
|
|
|
ClearView(); |
|
|
|
@ -1391,72 +1410,60 @@ namespace Avalonia.Controls |
|
|
|
// Determine if any filtering mode is on
|
|
|
|
bool stringFiltering = TextFilter != null; |
|
|
|
bool objectFiltering = FilterMode == AutoCompleteFilterMode.Custom && TextFilter == null; |
|
|
|
|
|
|
|
int view_index = 0; |
|
|
|
int view_count = _view!.Count; |
|
|
|
|
|
|
|
List<object> items = _items; |
|
|
|
foreach (object item in items) |
|
|
|
|
|
|
|
// cache properties
|
|
|
|
var textFilter = TextFilter; |
|
|
|
var itemFilter = ItemFilter; |
|
|
|
var _newViewItems = new Collection<object>(); |
|
|
|
|
|
|
|
bool success = false; |
|
|
|
|
|
|
|
await Task.Run(() => |
|
|
|
{ |
|
|
|
bool inResults = !(stringFiltering || objectFiltering); |
|
|
|
if (!inResults) |
|
|
|
foreach (object item in items) |
|
|
|
{ |
|
|
|
if (stringFiltering) |
|
|
|
// Exit the fitter when requested if cancellation is requested
|
|
|
|
if (_cancelRequested) |
|
|
|
{ |
|
|
|
inResults = TextFilter!(text, FormatValue(item)); |
|
|
|
return; |
|
|
|
} |
|
|
|
else |
|
|
|
|
|
|
|
bool inResults = !(stringFiltering || objectFiltering); |
|
|
|
|
|
|
|
if (!inResults) |
|
|
|
{ |
|
|
|
if (ItemFilter is null) |
|
|
|
if (stringFiltering) |
|
|
|
{ |
|
|
|
throw new Exception("ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom"); |
|
|
|
inResults = textFilter!(text, FormatValue(item)); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
inResults = ItemFilter(text, item); |
|
|
|
if (itemFilter is null) |
|
|
|
{ |
|
|
|
throw new Exception( |
|
|
|
"ItemFilter property can not be null when FilterMode has value AutoCompleteFilterMode.Custom"); |
|
|
|
} |
|
|
|
inResults = itemFilter(text, item); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (view_count > view_index && inResults && _view[view_index] == item) |
|
|
|
{ |
|
|
|
// Item is still in the view
|
|
|
|
view_index++; |
|
|
|
} |
|
|
|
else if (inResults) |
|
|
|
{ |
|
|
|
// Insert the item
|
|
|
|
if (view_count > view_index && _view[view_index] != item) |
|
|
|
if (inResults) |
|
|
|
{ |
|
|
|
// Replace item
|
|
|
|
// Unfortunately replacing via index throws a fatal
|
|
|
|
// exception: View[view_index] = item;
|
|
|
|
// Cost: O(n) vs O(1)
|
|
|
|
_view.RemoveAt(view_index); |
|
|
|
_view.Insert(view_index, item); |
|
|
|
view_index++; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
// Add the item
|
|
|
|
if (view_index == view_count) |
|
|
|
{ |
|
|
|
// Constant time is preferred (Add).
|
|
|
|
_view.Add(item); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_view.Insert(view_index, item); |
|
|
|
} |
|
|
|
view_index++; |
|
|
|
view_count++; |
|
|
|
_newViewItems.Add(item); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (view_count > view_index && _view[view_index] == item) |
|
|
|
{ |
|
|
|
// Remove the item
|
|
|
|
_view.RemoveAt(view_index); |
|
|
|
view_count--; |
|
|
|
} |
|
|
|
|
|
|
|
// set success to true if we reached the end of the loop
|
|
|
|
success = true; |
|
|
|
}); |
|
|
|
|
|
|
|
// add new view items if filtering was successful
|
|
|
|
if (success && _view != null) |
|
|
|
{ |
|
|
|
_view.Clear(); |
|
|
|
_view.AddRange(_newViewItems); |
|
|
|
} |
|
|
|
|
|
|
|
// Clear the evaluator to discard a reference to the last item
|
|
|
|
@ -1464,6 +1471,10 @@ namespace Avalonia.Controls |
|
|
|
{ |
|
|
|
_valueBindingEvaluator.ClearDataContext(); |
|
|
|
} |
|
|
|
|
|
|
|
// indicate that filtering is not ongoing anymore
|
|
|
|
_filterInAction = false; |
|
|
|
_cancelRequested = false; |
|
|
|
} |
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|