C-Sharpie
C-Sharpie

Reputation: 215

WPF Multi-Column Combobox

I know this question has been asked and I have looked at many results but the results I find don't seem to completely work or they give me issues.

What I want is a combo box that I can type and it will start to auto complete and narrow down based on a single column. For example if I had first name and last name I would type and the last names would narrow down.

With my example below, when I select a value in the combo box I get "System.Data.DataRowView". I have tested using the selection changed event just to check and I can't even set the text for the combo but If I can return the correct value from the data table.

If there are any suggestions for a good comprehensive resource to look up like a website, tutorial, video, online course, book I would be grateful.

  1. Search Combo box and auto update items
  2. Display a specific field with item selected

RESOURCE XAML:

 <Style x:Key="txtStyle" TargetType="{x:Type TextBlock}">
            <Setter Property="TextAlignment" Value="Left"></Setter>
            <Setter Property="FontSize" Value="14"></Setter>
            <Setter Property="HorizontalAlignment" Value="Left"></Setter>
        </Style>

 <DataTemplate x:Key="comboKEY">
            <Grid Height="25" Width="300" ShowGridLines="false">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0"></ColumnDefinition>
                    <ColumnDefinition Width="200"></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <TextBlock Grid.Column="0"  Text="{Binding Path='id'}"
                    Style="{StaticResource txtStyle}"></TextBlock>
                <TextBlock Grid.Column="1"  Text="{Binding Path='site_name'}"
                    Style="{StaticResource txtStyle}"
                           TextWrapping="Wrap"></TextBlock>
            </Grid>
        </DataTemplate>

XAML COMBO BOX

<WrapPanel Orientation="Horizontal" Canvas.Left="10" Canvas.Top="90">
                <ComboBox IsEditable="True"
                  Width="200"
                  Height="25"
                  IsTextSearchEnabled="False"
                  x:Name="cboName"
                  ItemTemplate="{StaticResource comboKEY
                  ItemsSource="{Binding}" SelectionChanged="cboName_SelectionChanged">
                </ComboBox>
            </WrapPanel>

CODE-BEHIND

 private void cboName_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            cboName.Text = dt.Rows[cboName.SelectedIndex]["dt_field"].ToString();
           
            test1.Content = dt[cboName.SelectedIndex]["dt_field"].ToString();
        }

Upvotes: 1

Views: 1378

Answers (1)

BionicCode
BionicCode

Reputation: 29028

When you want to filter or sort a DataTable, you should create a DataView and bind to it. Then use a filter expression assigned to DataView.RowFilter to dynamically filter the table.

The following example shows how to filter based on text input by handling TextBox.TextChanged raised by the editable TextBox of the ComboBox.
The column to filter is also made dynamic.

Since the ComboBox.IsEditable is set to True, the selection box's ContenPresenter is replaced by a TextBox. This means you only can display text. By default the ComboBox will call object.ToString(). If the item like a DataRowView doesn't provide a usable override, you are left to set the ComboBox.DisplayMemberPath. Setting this property disallows to set the ComboBox.ItemTemplate. If you still want to layout the drop down items, you must override the ComboBox.ItemContainerStyle.

MainWindow.xaml.cs

partial class MainWindow : Window
{
  // This property should be a DependencyProperty
  public DataView DataTableView { get; set; }

  // This property should be a DependencyProperty and have a property changed callback to update the DataTableView filter registered
  public string FilteredColumnName { get; set; }

  public MainWindow()
  { 
    InitializeComponent();
    this.DataContext = this;

    var dataTable = new DataTable();
   
    // TODO::Populate DataTable.
    // Assume table has a column 'LastName'

    this.DataTableView = dataTable.DefaultView;

    // Filter based on column 'LastName'
    this.FilteredColumnName = "LastName";
  }

  private void FilterDataTable_OnTextInput(object sender, TextChangedEventArgs textChangedEventArgs)
  {
    var comboBox = sender as ComboBox;
    var dataview = comboBox.ItemsSource as DataView;

    // Filter based on column 'LastName', 
    // where the value starts with the entered text: '<columnName> LIKE '<textInput>*'
    dataview.RowFilter = $"{this.FilteredColumnName} LIKE '{(textChangedEventArgs.OriginalSource as TextBox).Text}*'";
  }
}

MainWindow.xaml

<Window>
  <ComboBox ItemsSource="{Binding DataTableView}"
            DisplayMemberPath="LastName"
            SelectedValuePath="LastName"
            IsEditable="True" 
            StaysOpenOnEdit="True"
            TextBox.TextChanged="FilterDataTable_OnTextInput">
    <ComboBox.ItemContainerStyle>
      <Style TargetType="{x:Type ComboBoxItem}">
        <Setter Property="Template">
          <Setter.Value>
            <ControlTemplate TargetType="{x:Type ComboBoxItem}">
              <Border BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}"
                      Background="{TemplateBinding Background}">
                <StackPanel Orientation="Horizontal">
                  <TextBlock Text="{Binding Name}" />
                  <TextBlock Text="{Binding LastName}" />
                </StackPanel>
              </Border>
            </ControlTemplate>
          </Setter.Value>
        </Setter>
      </Style>
    </ComboBox.ItemContainerStyle>
  </ComboBox>
</Window>

Upvotes: 1

Related Questions