Как заполнить элементы ComboBox при изменении текста в одном и том же комбинированном окне - PullRequest
0 голосов
/ 04 марта 2020

Я пытаюсь реализовать событие textchanges в комбинированном ящике, чтобы элементы изменялись в зависимости от значения, введенного в том же комбинированном ящике.

Вот мой XAML

<ComboBox Name="ComboBoxRoleNameDescEdit" IsEditable="True" TextBoxBase.TextChanged="ComboBoxRoleNameDescEdit_TextChanged"/>

И здесь это код позади:

private void ComboBoxRoleNameDescEdit_TextChanged(object sender, TextChangedEventArgs e)
{
    try
    {
        ComboBoxRoleNameDescEdit.Items.Clear();
        using (var Connect = new SqlConnection(connstr))
        {
            Connect.Open();
            using (var Command = new SqlCommand("[dbo].[spParametresRolesTb_FillRoleIdComboBox]", Connect))
            {
                Command.CommandType = CommandType.StoredProcedure;
                Command.Parameters.Add("@search", SqlDbType.VarChar).Value = ComboBoxRoleNameDescEdit.Text;
                Command.Parameters.Add("@entity_id", SqlDbType.VarChar).Value = LoggedInData.LoggedInstitutionId;
                SqlDataReader dr = Command.ExecuteReader();
                while (dr.Read())
                {
                    string classes = dr.GetString(0);
                    ComboBoxRoleNameDescEdit.Items.Add(classes);
                }
                dr.Close();
            }
            Connect.Close();
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }

Проблема, с которой я сталкиваюсь, состоит в том, что, поскольку мне нужно обновить sh список элементов на каждом входе, сам вход очищается из-за этой части code `` ComboBoxRoleNameDescEdit.Items.Clear (); `` `. на самом деле автоматически очищается только первый вход, остальные - нет.

Есть идеи, как мне это преодолеть?

1 Ответ

0 голосов
/ 04 марта 2020

Прежде всего, не работайте с 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;
  }
}

Ваша текущая реализация не очень эффективна и предлагает плохой пользовательский опыт. При каждом нажатии клавиши обработчик события будет читать, возможно, много данных, что приведет к зависанию интерфейса. Я рекомендую асинхронный подход. Возможно, даже добавьте некоторую задержку, чтобы не каждое нажатие клавиши при наборе слова включало фильтр.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...