I am trying to implement a search (Ctrl+F) feature on a XamDataGrid. How can I programmatically invoke record filtering on the grid that searches across content in multiple columns and displays only columns that match the search?
相关问题
- VNC control for WPF application
- WPF Binding from System.Windows.SystemParameters.P
- XAML: Applying styles to nested controls
- How can I add a horizontal line (“goal line”) for
- How to properly change a resource dictionary
I was looking for a very basic solution, so I'll share what has worked for me. Here is my C# code-behind code to apply a filter to a column:
Here grdConnectionManagerEntries is my XamDataGrid, I have a Field (i.e. column) called Name, and I'm adding a Contains filter to it that will filter by the given connectionName.
For some reason the filter text only shows up in the XamDataGrid's UI when I use a Contains comparison operator; I'm guessing because that's the default filter operator on that column. So there's something that I'm not doing quite right, but since I wanted to use the Contains operator anyways it has worked for my purposes.
The record filtering in the DataPresenter is just that - a means of the filtering the records based on some specified criteria. Normally that criteria is provided via one of the built in ui's - either using the LabelIcons which is just a drop down list of the filtered values or via the FilterRecord which is a dedicated special record that is displayed with cells for each column to allow choosing/entering an operator and value.
That being said the record filtering can be manipulated in code if you wish. The FieldLayout exposes a RecordFilters collection where a RecordFilter provides the conditions (i.e. the match criteria) and the field for which the match should be performed. It is also exposed off the RecordManager but this is really to facilitate filtering in hierarchical situations where the filter criteria is different for each "island" of child records.
Since you want to search multiple fields for the same criteria, you would need to create a RecordFilter for each Field in the FieldLayout's Fields collection (or whatever subset of the Fields to which you want this to be applied). Since you want to do a text search you probably want to use a ComparisonCondition where the ComparisonOperator you use is Contains and the value would be the text to search for. Now since you want a record to match if the value is found in any of the fields (for which you have created a RecordFilter) you will also need to set the FieldLayoutSettings' RecordFiltersLogicalOperator property to Or (by default this resolves to And since one typically wants to match a record when all the criteria matches the entered values).
So to that end the following is a basic attached behavior (in this case a property named FilterText that you would set on the DataPresenter to be filtered). This behavior/property will manipulate the RecordFilters of the DefaultFieldLayout considering the text value of the FilterText property.
You can then do something like the following to hook up the value of a TextBox to this attached property. Note, you would need to define an xmlns mapping for local to be whatever clr namespace you put the above class into.
Now the other part of your question was about only showing the columns which contained matching values. This isn't really possible as part of the record filters itself since record filtering is about filtering out records based on some specified criteria and has no relation to hiding/showing columns/fields. That being said perhaps one way to help the end user understand where the matching text resides would be to highlight that text within the cells.
To provide such functionality I defined a derived TextBlock called HighlightTextBlock. It exposes several properties:
Here is the code for the class:
So then you could change the templates for the editors you are using to use this in their render template. e.g.
The way that I would approach this problem would be to add code to the search button click event (or some other trigger mechanism) and perform the search directly in code, using linq or record by record comparison, whatever works best in your situation (it would help to know how the columns and rows are populated).
Once you have the set of records and/or columns that have the appropriate results, hide the columns that do not have hits. To streamline the process, I would create a collection of columns that should be hidden, then have a method that processes each column. The method would search through the column data and at the sign of the first match, would remove the column from the list of columns to be hidden and stop processing records in the column.
At the end of the process, your list would contain the list of columns to hide, which you could then use to perform the actual hiding.
This mechanism could also be extended to hide rows that don't have any matches, assuming that there are multiple rows. I would support this by hiding each of the rows that don't have a match while processing the first column and then unhiding them for subsequent columns where a match was found.
Thank you for sharing. I'll share too. I used this example to handle a more simple case. I just wanted to set up a filter on one column. Here it is:
And the XAML:
This works perfectly for me.