Karl_Schuhmann
Karl_Schuhmann

Reputation: 1332

Filter a DataGrid in WPF

I load a lists of objects in a datagrid with this:

dataGrid1.Items.Add(model);

The model become data from a database. It has a Id(int), Name(string) and Text(string)

In my datagrid I show only the Name of the model. How can I filter the datagrid now, when I enter something in a textbox?

I was at this page: http://msdn.microsoft.com/en-us/library/vstudio/ff407126(v=vs.100).aspx but I don't understand the code from there and I can not explain how I should transpose that for my problem.

Upvotes: 24

Views: 100228

Answers (7)

Joee
Joee

Reputation: 2016

You can use dataview filter in order to filter the datagrid rows.

DataView dv = datatable.DefaultView;
StringBuilder sb = new StringBuilder();
foreach (DataColumn column in dv.Table.Columns)
{
    sb.AppendFormat("[{0}] Like '%{1}%' OR ", column.ColumnName, "FilterString");
}
sb.Remove(sb.Length - 3, 3);
dv.RowFilter = sb.ToString();
dgvReports.ItemsSource = dv;
dgvReports.Items.Refresh();

Where the "datatable" is datasource given to your datagrid and using string builder you build the filter query where "Filter String" is the text you want to search in your datagrid and set it to dataview and finally set the dataview as itemsource to your datagrid and refresh it.

Upvotes: 0

Aaron
Aaron

Reputation: 77

Leaving this here incase like me you couldn't get the other methods above to work properly.

//Example of my Transaction class
//Transaction(ID, Amount, Description)

ListCollectionView collectionView = new ListCollectionView(List<Transaction>);
collectionView.Filter = (e) =>
{
    Transaction transaction = e as Transaction;
    if (transaction.Amount >= 0) //When this is true it returns all positive transactions
    {
       return true;
    }
    else
    {
       return false;
    }
};

dataGrid.ItemsSource = collectionView;

  • I used this enclosed in if statements to make a filter by combobox for the datagrid.
  • The combobox has 3 options Credit/Debit/Credit & Debit, depending on the option I chose it would filter on a selection change event
  • I got this from here: http://dotnetpattern.com/wpf-datagrid-filtering

Upvotes: 0

DJakub
DJakub

Reputation: 51

I found a dumb method and know this is an old question but ... Just use the Filter function on items property on the DataGrid object. Like this: (I'm sorry but i learned only VB)

Public Property SearchName As String
    Get
        Return _SearchName
    End Get
    Set
        _SearchName = Value
        DG_drw_overview.Items.Filter = New Predicate(Of Object)(Function(x) x.Name.Contains(Value))           
    End Set
End Property

This property is changed every time you type something in the textbox. DG_drw_overview is the DataGrid instance. In Predicate the object represents the object you put in the DataGrid.

Then bind the SearchName to textbox

<TextBox x:Name="TB_search" 
         Text="{Binding SearchName, UpdateSourceTrigger=PropertyChanged}"/>

Set datacontext of the textbox to the main class (usually after InitializeComponent())

TB_search.DataContext = Me

Upvotes: 1

blindmeis
blindmeis

Reputation: 22445

take at look at DataBinding --> in your case dont add items to your grid, but set the itemssource

<Datagrid ItemsSource="{Binding MyCollectionOfModels}" />

or

dataGrid1.ItemsSource = this._myCollectionOfModels;

and if you want some kind of filtering,sorting, grouping look at CollectionView

Upvotes: 0

steve
steve

Reputation: 150

@WiiMaxx, can't comment as not enough rep. I would be a bit more careful about the direct casts there. They can be slow for one thing and for another, if the same filter was applied to a grid holding different complex type data you would have an InvalidCastException.

// your Filter
    var yourCostumFilter= new Predicate<object>(item =>
    {
        item = item as Model;
        return item == null || item.Name.Contains("Max");
    });

This will not break you datagrid and will not filter the results if the cast fails. Less impact to your users if you get the code wrong. On top of that the filter will be faster due to the "as" operator not doing any explicit type coercion as the direct cast operation will.

Upvotes: 5

WiiMaxx
WiiMaxx

Reputation: 5420

there are multiple way's to filter Collection

let's suggesting this is your Item Class

public class Model
{
    public string Name
    {
        get;
        set;
    }
}

and your collection looks like

       var ObColl = new ObservableCollection<Model>();

        ObColl.Add(new Model() { Name = "John" });
        ObColl.Add(new Model() { Name = "Karl" });
        ObColl.Add(new Model() { Name = "Max" });
        ObColl.Add(new Model() { Name = "Mary" });

Way 1 (Predicate):

    public MainWindow()
    {
        InitializeComponent();

        // Collection which will take your ObservableCollection
        var _itemSourceList = new CollectionViewSource() { Source = ObColl };

        // ICollectionView the View/UI part 
        ICollectionView Itemlist = _itemSourceList.View;

        // your Filter
        var yourCostumFilter= new Predicate<object>(item => ((Model)item).Name.Contains("Max"));

        //now we add our Filter
        Itemlist.Filter = yourCostumFilter;

        dataGrid1.ItemsSource = Itemlist;
    }

Way 2 (FilterEventHandler):

    public MainWindow()
    {
        InitializeComponent();

        // Collection which will take your Filter
        var _itemSourceList = new CollectionViewSource() { Source = ObColl };

       //now we add our Filter
       _itemSourceList.Filter += new FilterEventHandler(yourFilter);

        // ICollectionView the View/UI part 
        ICollectionView Itemlist = _itemSourceList.View;

        dataGrid1.ItemsSource = Itemlist;
    }

    private void yourFilter(object sender, FilterEventArgs e)
    {
        var obj = e.Item as Model;
        if (obj != null)
        {
            if (obj.Name.Contains("Max"))
                e.Accepted = true;
            else
                e.Accepted = false;
        }
    }

extended Information to Way 1

if need multiple conditions or some complex Filter you can add a method to your Predicat

    // your Filter
    var yourComplexFilter= new Predicate<object>(ComplexFilter);

    private bool ComplexFilter(object obj)
    {
        //your logic
    }

Upvotes: 56

Lor&#225;nd Bir&#243;
Lor&#225;nd Bir&#243;

Reputation: 856

This is a simple implementation of using the Filter property of ICollectionView. Suppose your XAML contains this:

<TextBox x:Name="SearchTextBox" />
<Button x:Name="SearchButton"
        Content="Search"
        Click="SearchButton_OnClick"
        Grid.Row="1" />
<DataGrid x:Name="MyDataGrid"
          Grid.Row="2">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Lorem ipsum column"
                            Binding="{Binding}" />
    </DataGrid.Columns>
</DataGrid>

Then in the constructor you can get the default view for your data where you can set the filter predicate which will be executed for every item of your collection. The CollectionView won't know when it should update the collection, so you have to call Refresh when the user clicks the search button.

private ICollectionView defaultView;

public MainWindow()
{
    InitializeComponent();

    string[] items = new string[]
    {
        "Asdf",
        "qwer",
        "sdfg",
        "wert",
    };

    this.defaultView = CollectionViewSource.GetDefaultView(items);
    this.defaultView.Filter =
        w => ((string)w).Contains(SearchTextBox.Text);

    MyDataGrid.ItemsSource = this.defaultView;
}

private void SearchButton_OnClick(object sender, RoutedEventArgs e)
{
    this.defaultView.Refresh();
}

At this url you can find a more detailed description of CollectionViews: http://wpftutorial.net/DataViews.html

Upvotes: 9

Related Questions