首页 > 解决方案 > 当同一组合框中的文本更改时如何填充组合框项目

问题描述

我正在尝试将 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);
    }

我遇到的问题是,因为我需要在每个输入处刷新项目列表,由于这段代码“ComboBoxRoleNameDescEdit.Items.Clear();”,输入本身正在被清除。实际上,只有第一个输入会被自动清除,其余的输入不会。

知道如何克服这个问题吗?

标签: c#wpfxamlcombobox

解决方案


首先,不要ItemsControl直接操作。对源集合即数据模型进行操作。您还需要在ComboBox.

视图模型.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);
          }
        }
      }
    }  
  }
}

主窗口.xaml

<Window>
  <Window.DataContext>
    <ViewModel />
  </Window.DataContext>

  <ComboBox IsTextSearchEnabled="False" 
            IsEditable="True" 
            ItemsSource="{Binding Classes}"
            TextBoxBase.TextChanged="ComboBox_TextChanged" />
</Window>

主窗口.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;
  }
}

您当前的实现效率不高,并且提供了糟糕的用户体验。在每次按键时,事件处理程序可能会读取大量数据,这将冻结 UI。我推荐一种异步方法。甚至可能添加一些延迟,以便在键入单词时并非每次按键都会触发过滤器。


推荐阅读