Home > Blockchain >  Datagrid with Autocomplete combobox - again
Datagrid with Autocomplete combobox - again

Time:07-21

I understand this has been asked several times but I am unable to find a toolkit that allows for a combobox in a WPF DataGrid that allows to filter and autocomplete, none of the questions provide any solution that I was able to make work. The behavior I need in my combobox column is:

  • Pressing on drop down of the cell will show all objects in the ItemSource.
  • As soon as you press on the "text box" portion of the combobox it would filter the drop down as you enter/delete text and depending on the text in the combobox.
  • Entering something invalid would show "not found" in the drop down. Pressing out or losing focus would just retain the last valid item in that combobox.

I have tried several options that I couldnt either get to work with a datagrid or they simply did not have the requirements above. Examples are:

  1. DotNetProjects.WpfToolkit
  2. Syncfusion.ComboBoxAdv

I have created a simple test application to test several options. My models are a Fruit and Vegetable object with just one property (Name). I have a Customer object that has a name and item (fruit or vegetable). Models

public class Vegetable : IsFruitOrVegetable, INotifyPropertyChanged
{
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            RaisePropertyChanged("Name");
        }
    }

    public Vegetable()
    {

    }

    public Vegetable(string _Name)
    {
        this.Name = _Name;
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string PropertyName)
    {
        var property = PropertyChanged;
        if (property != null)
            property(this, new PropertyChangedEventArgs(PropertyName));
    }
}

public class Fruit: IsFruitOrVegetable, INotifyPropertyChanged
{
    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            RaisePropertyChanged("Name");
        }
    }

    public Fruit()
    {

    }

    public Fruit(string _Name)
    {
        this.Name = _Name;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string PropertyName)
    {
        var property = PropertyChanged;
        if (property != null)
            property(this, new PropertyChangedEventArgs(PropertyName));
    }

}

public class Customer : INotifyPropertyChanged
{

    private string name;

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            RaisePropertyChanged("Name");
        }
    }

    private IsFruitOrVegetable item;

    public IsFruitOrVegetable Item
    {
        get
        {
            return item;
        }
        set
        {
            item = value;
            RaisePropertyChanged("Item");
        }
    }

    private int totalQuantity;

    public int TotalQuantity
    {
        get
        {
            return totalQuantity;
        }
        set
        {
            totalQuantity = value;
            RaisePropertyChanged("TotalQuantity");
        }
    }

    public Customer()
    {

    }

    public Customer(string _Name)
    {
        this.Name = _Name;
        Item = null;
        TotalQuantity = 1;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string PropertyName)
    {
        var property = PropertyChanged;
        if (property != null)
            property(this, new PropertyChangedEventArgs(PropertyName));
    }
}

My viewmodel consists of a list of my customers (CustomersList) and itemsList (list of items for fruits and vegetables).

public class MainViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Customer> customersList;

    public ObservableCollection<Customer> CustomersList
    {
        get
        {
            return customersList;
        }
        set
        {
            customersList1 = value;
        }
    }

    private ObservableCollection<IsFruitOrVegetable> itemsList;

    public ObservableCollection<IsFruitOrVegetable> ItemsList
    {
        get
        {
            return itemsList;
        }
        set
        {
            itemsList = value;
        }
    }

    private IsFruitOrVegetable itemOption;

    public IsFruitOrVegetable ItemOption
    {
        get
        {
            return itemOption;
        }
        set
        {
            itemOption = value;
        }
    }

    public MainViewModel()
    {
        ItemsList = new ObservableCollection<IsFruitOrVegetable>();
        ItemsList.Add(new Fruit("Apple"));
        ItemsList.Add(new Fruit("Banana"));
        ItemsList.Add(new Fruit("Avocado"));
        ItemsList.Add(new Fruit("Blueberries"));

        ItemsList.Add(new Vegetable("Broccoli"));
        ItemsList.Add(new Vegetable("Cabbage"));
        ItemsList.Add(new Vegetable("Carrot"));
        ItemsList.Add(new Vegetable("Cauliflower"));

        CustomersList = new ObservableCollection<Customer>();
        CustomersList.Add(new Customer("Bob"));
        CustomersList.Add(new Customer("Tony"));
        CustomersList.Add(new Customer("John"));

        CustomersList[0].Item = ItemsList[0];
        CustomersList[1].Item = ItemsList[1];
        CustomersList[2].Item = ItemsList[2];
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged(string PropertyName)
    {
        var property = PropertyChanged;
        if (property != null)
            property(this, new PropertyChangedEventArgs(PropertyName));
    }
}

My view for:

        <Label Content="DEFAULT COMBOBOX" HorizontalAlignment="Left" Height="32" Margin="22,10,0,0" VerticalAlignment="Top" Width="207"/>
    <ComboBox HorizontalAlignment="Left" Margin="22,47,0,0" VerticalAlignment="Top" Width="120" DisplayMemberPath="Name"
              SelectedValue="{Binding ItemOption, Source={StaticResource viewModel}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
              ItemsSource="{Binding ItemsList,Source={StaticResource viewModel}}"/>

    <DataGrid HorizontalAlignment="Left" Height="291" Margin="22,90,0,0" VerticalAlignment="Top" Width="207"
              ItemsSource="{Binding CustomersList,Source={StaticResource viewModel}}" AutoGenerateColumns="False">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Customer" Binding="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="*"/>
            <DataGridComboBoxColumn Header="Item" ItemsSource="{Binding ItemsList,Source={StaticResource viewModel}}" Width="*"
                                DisplayMemberPath="Name"
                                SelectedItemBinding="{Binding Item, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </DataGrid.Columns>
    </DataGrid>

I know alot of solutions implement a template column but I cant find a combobox that works with the intent I want to implement this. Can someone provide a super simple example that allows or point me in the right direction?

CodePudding user response:

I have done something similar, try https://github.com/DamnjanMarkovic/WPFMedicalData.

CodePudding user response:

The behaviour is very specific. If you do not find any ComboBoxes, that behave the correct way you always can create it by yourself.

One Way could be a ToggleButton in Combination with an Popup. For Reusability you also can put it into an UserControl

Here a very minimalistic example, how you could start creating the control by yourself (not functional and tested).

        <ToggleButton x:name="MyButton" />
    <Popup
        PlacementTarget="{Binding ElementName=MyButton}"
        Width="{Binding ActualWidth, ElementName=MyButton}">
        <Border
            BorderBrush="Black"
            BorderThickness="2"
            Background="White">
            <StackPanel>
                <!-- Some Other Controlls like an TextBox for searching the contents -->
                <!-- List View for displaying the ItemSource -->
            </StackPanel>
        </Border>
    </Popup>

To impement it into a Datagrid i would Recomment the TemplateCollumn.

<DataGridTemplateColumn Header="Header">
<DataGridTemplateColumn.CellTemplate>
    <DataTemplate>
        <uc:CustomUserControl />
    </DataTemplate>
</DataGridTemplateColumn.CellTemplate>

Note that for the ItemSource requires a relative source if it is not part of your items model.

ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type DataGrid}, Mode=FindAncestor}, Path=DataContext.YourItemSource}"
  • Related