Прежде всего, не работайте с ItemsControl
напрямую. Работать с исходной коллекцией, т.е. с моделью данных. Также вам нужно отключить поиск текста на ComboBox
.
ViewModel.cs
class ViewModel
{
public ObservableCollection<string> Classes { get; set; }
public ViewModel()
{
this.Classes = new ObservableCollection<string>();
}
public async Task FilterItemsAsync(string predicate, CancellationToken cancellationToken)
{
using (var connection = new SqlConnection(connstr))
{
connection.OpenAsync(cancellationToken);
using (var command = new SqlCommand("[dbo].[spParametresRolesTb_FillRoleIdComboBox]", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@search", SqlDbType.VarChar).Value = predicate;
command.Parameters.Add("@entity_id", SqlDbType.VarChar).Value = LoggedInData.LoggedInstitutionId;
using (SqlDataReader dataReader = await command.ExecuteReaderAsync(cancellationToken))
{
this.Classes.Clear();
while (await dataReader.ReadAsync(cancellationToken))
{
string classes = dataReader.GetString(0);
this.Classes.Add(classes);
}
}
}
}
}
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<ComboBox IsTextSearchEnabled="False"
IsEditable="True"
ItemsSource="{Binding Classes}"
TextBoxBase.TextChanged="ComboBox_TextChanged" />
</Window>
MainWindow.xaml.cs
partial class MainWindow : Window
{
private DispatcherTimer DelayTimer { get; set; }
private CancellationTokenSource CancellationTokenSource { get; set; }
private string FilterPredicate { get; set; }
private bool IsFilterExecuting { get; set; }
public MainWindow()
{
InitializeDelayTimer();
this.FilterPredicate = string.Empty;
}
private void InitializeDelayTimer()
{
this.DelayTimer = new DispatcherTimer();
this.DelayTimer.Tick += FilterItemsAsync_OnTimerElapsed;
this.DelayTimer.Interval = TimeSpan.FromMilliseconds(800);
}
private void ComboBox_TextChanged(object sender, TextChangedEventArgs e)
{
if (!(e.OriginalSource is TextBox textBox))
{
return;
}
HandleTimer();
TryCancelRunningFilter();
this.FiterPredicate = textBox.Text;
}
private async void FilterItemsAsync_OnTimerElapsed(object sender, EventArgs e)
{
this.IsFilterExecuting = true;
var viewModel = this.DataContext as ViewModel;
try
{
this.CancellationTokenSource = new CancellationTokenSource();
await viewModel.FilterItemsAsync(this.FiterPredicate, this.CancellationTokenSource.Token);
}
catch (OperationCanceledException) {}
finally
{
this.DelayTimer.Stop();
this.CancellationTokenSource.Dispose();
this.IsFilterExecuting = false;
}
}
private void HandleTimer()
{
// Start or reset the timer interval
this.DelayTimer.Start();
}
private bool TryCancelRunningFilter()
{
if (this.IsFilterExecuting)
{
this.CancellationTokenSource?.Cancel();
return true;
}
return false;
}
}
Ваша текущая реализация не очень эффективна и предлагает плохой пользовательский опыт. При каждом нажатии клавиши обработчик события будет читать, возможно, много данных, что приведет к зависанию интерфейса. Я рекомендую асинхронный подход. Возможно, даже добавьте некоторую задержку, чтобы не каждое нажатие клавиши при наборе слова включало фильтр.