HI
This is longer post, sorry. First I'll explain what I'm trying to do, and then I'll tell a little about what I have tried.
The Problem
I have an object with a property that consist of a list of strings. Showing it in a RadGridView is not hard.
<
RadGridView
...>
<
GridViewBoundColumnBase
Header
=
"Tags"
DataMemberBinding
=
"{Binding CategoryTagLabels, Converter={StaticResource ListToStringConverter}}"
/>
</
RadGridView
>
The ListToStringConverter basically just runs string.join(",", tags).
Now, that is mighty fine.
The problem is that I would like to add filtering. I would for the list of distinct values to be shown, and rows that that has any of the selected values from distinct values present in their CategoryTagLabels should be shown.
My Attempt
I tried to follow Custom Filtering Controls and the FilteringCollectionProperties_WPF from your sample SDK
<
RadGridView
...>
<
GridViewBoundColumnBase
Header
=
"Tags"
DataMemberBinding
=
"{Binding CategoryTagLabels, Converter={StaticResource ListToStringConverter}}"
>
<
GridViewBoundColumnBase
.FilteringControl>
<
StringListFilterControl
FilterMemberName
=
"CategoryTagLabels"
/>
</
GridViewBoundColumnBase
.FilteringControl>
</
GridViewBoundColumnBase
</RadGridView>
StringListFilterControl.xaml
<
UserControl
x:Class
=
"Core.Controls.ThirdParty.Telerik.StringListFilterControl"
xmlns:local
=
"clr-namespace:Core.Controls.ThirdParty.Telerik"
MinWidth
=
"100"
MinHeight
=
"100"
mc:Ignorable
=
"d"
d:DesignHeight
=
"450"
d:DesignWidth
=
"800"
>
<
StackPanel
>
<
ListView
ItemsSource
=
"{Binding Values}"
x:Name
=
"TagLabels"
>
</
ListView
>
<
Button
Content
=
"Apply Filter"
Click
=
"ApplyFilter"
></
Button
>
<
Button
Content
=
"Clear Filter"
Click
=
"ClearFilter"
></
Button
>
</
StackPanel
>
</
UserControl
>
StringListFilterControl.xaml.cs
public
partial
class
StringListFilterControl : UserControl, IFilteringControl, INotifyPropertyChanged
{
private
ICollection<
string
> _values;
private
GridViewBoundColumnBase _column;
private
CompositeFilterDescriptor _filterDescriptor;
public
StringListFilterControl()
{
InitializeComponent();
this
.DataContext =
this
;
}
public
void
Prepare(GridViewColumn columnToPrepare)
{
_column = columnToPrepare
as
GridViewBoundColumnBase;
if
(_column ==
null
)
{
return
;
}
var sender = _column.DataControl;
var columnValues = ((RadGridView)sender).GetDistinctValues(_column,
false
);
var values =
new
List<
string
>();
foreach
(var item
in
columnValues)
{
if
(item ==
null
)
continue
;
values.AddRange((IEnumerable<
string
>)item);
}
var distinctValues = values.Distinct().ToList();
Values = distinctValues;
}
public
static
readonly
DependencyProperty IsActiveProperty = DependencyProperty.Register(
"IsActive"
,
typeof
(
bool
),
typeof
(StringListFilterControl),
new
PropertyMetadata(
default
(
bool
)));
public
bool
IsActive
{
get
{
return
(
bool
) GetValue(IsActiveProperty); }
set
{ SetValue(IsActiveProperty, value); }
}
public
ICollection<
string
> Values
{
get
=> _values;
set
{
_values = value;
this
.RaisePropertyChanged();
}
}
public
static
readonly
DependencyProperty FilterMemberNameProperty = DependencyProperty.Register(
"FilterMemberName"
,
typeof
(
string
),
typeof
(StringListFilterControl),
new
PropertyMetadata(
default
(
string
)));
public
string
FilterMemberName
{
get
{
return
(
string
) GetValue(FilterMemberNameProperty); }
set
{ SetValue(FilterMemberNameProperty, value); }
}
public
event
PropertyChangedEventHandler PropertyChanged;
private
void
ApplyFilter(
object
sender, RoutedEventArgs e)
{
if
(_filterDescriptor ==
null
)
{
_filterDescriptor =
new
CompositeFilterDescriptor();
_filterDescriptor.LogicalOperator = FilterCompositionLogicalOperator.Or;
}
if
(!_column.DataControl.FilterDescriptors.Contains(_filterDescriptor))
{
_column.DataControl.FilterDescriptors.Add(_filterDescriptor);
}
_filterDescriptor.FilterDescriptors.Clear();
var selectedTagLabels = TagLabels.SelectedItems;
foreach
(var selectedTagLabel
in
selectedTagLabels)
{
var tagLabel = (
string
) selectedTagLabel;
var filter =
new
TagLabelFilterDescriptor
{
FilterMemberName = FilterMemberName,
TagLabel = tagLabel
};
_filterDescriptor.FilterDescriptors.Add(filter);
}
IsActive =
true
;
}
private
void
ClearFilter(
object
sender, RoutedEventArgs e)
{
_column.DataControl.FilterDescriptors.Clear();
IsActive =
false
;
}
}
public
class
TagLabelFilterDescriptor : IFilterDescriptor
{
private
static
readonly
MethodInfo EnumerableCastMethod =
typeof
(Enumerable).GetMethod(
"Cast"
);
private
static
MethodInfo GenericContainsMethod = GetGenericContainsMethodInfo();
public
event
PropertyChangedEventHandler PropertyChanged;
public
string
TagLabel {
get
;
set
; }
public
string
FilterMemberName {
get
;
set
; }
public
Expression CreateFilterExpression(Expression instance)
{
MemberExpression collectionPropertyAccessor = Expression.Property(instance, FilterMemberName);
MethodCallExpression genericCollectionPropertyAccessor = Expression.Call(
null
, EnumerableCastMethod.MakeGenericMethod(
new
[] {
typeof
(
object
) })
, collectionPropertyAccessor);
ConstantExpression tagLabel = Expression.Constant(TagLabel);
var expression = Expression.Call(
GenericContainsMethod,
genericCollectionPropertyAccessor,
tagLabel);
return
expression;
}
private
static
MethodInfo GetGenericContainsMethodInfo()
{
// get the Enumerable.Contains<TSource>(IEnumerable<TSource> source, TSource value) method,
// because it is impossible to get it through Type.GetMethod().
var methodCall = ((MethodCallExpression)
(
(Expression<Func<IEnumerable<
object
>,
bool
>>)(source => source.Contains(
null
))
).Body).Method.GetGenericMethodDefinition();
return
methodCall.MakeGenericMethod(
typeof
(
object
));
}
}
And this actually works and I like that it is (almost) something I can just attach to a column if the property is a list of string. This is nice as I have properties of collections of strings in other tables, so it is nice when it is easy to reuse the code.
Only problem is that I now have to reimplement the filtering UI from scratch. First of I have to make it look like the other filter controls (which is hard enough) but it also mean that any styling done for the standard filtering control, will have to maintained separately for StringListFilteringControl.
Is there any way to reuse the standard filtering control?