In the RadGridView Filtering in the Filter Row Mode. I am trying to add a "filter clear button" in to the Filtering TextBoxes. I know I may have to add this new clear button into the StringFilterEditorTemplate and also figure out a way to add it to the dynamically generated TextBox. (please see my snippet showing where I maybe able to tap in to this). My main question here is is there a way to do this and Bind it to the ClearFilter method. I prefer this is all done from XAML and maybe an attached behavior.
Please share if you have an example.
Thanks!
/* For the StringFilterEditorTemplate */
<ControlTemplate x:Key="StringFilterEditorTemplate" TargetType="Editors:StringFilterEditor">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<telerik:RadToggleButton Style="{StaticResource RadToggleButtonFilterStyle}" Content="aA" Grid.Column="1" IsChecked="{Binding IsCaseSensitive, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="-3,0,0,0" Visibility="{Binding MatchCaseVisibility, RelativeSource={RelativeSource TemplatedParent}}" >
<ToolTipService.ToolTip>
<ToolTip telerik:LocalizationManager.ResourceKey="GridViewFilterMatchCase" />
</ToolTipService.ToolTip>
</telerik:RadToggleButton>
<TextBox Grid.Column="0" Text="{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" telerik:TextBoxBehavior.UpdateTextOnEnter="True" />
</Grid>
</ControlTemplate>
/* For the Dynamically generated TextBoxes*/
private void OnFieldFilterEditorCreated(object sender, EditorCreatedEventArgs e)
{
var filterTextBox = e.Editor as TextBox;
if (filterTextBox != null)
{
filterTextBox.Style = (Style)Application.Current.Resources["TextBoxStyle"];
6 Answers, 1 is accepted
There is a way to achieve this but it will involve a lot of hacks.
Clearing the filter is not particularly related to the concrete type of field filter editor, i.e. StringFilterEditor (for strings), RadDateTimePicker (for DateTimes), TextBox (for everything else) so it might be a good idea to place this button next to and not inside the field filter editor.
Here is the ControlTemplate of the entire control which you see in the header cell:
<
ControlTemplate
x:Key
=
"FieldFilterControlTemplate"
TargetType
=
"grid:FieldFilterControl"
>
<
Border
BorderBrush
=
"{TemplateBinding BorderBrush}"
BorderThickness
=
"{TemplateBinding BorderThickness}"
>
<
Grid
x:Name
=
"PART_MainGrid"
>
<
Grid.ColumnDefinitions
>
<
ColumnDefinition
Width
=
"*"
/>
<
ColumnDefinition
Width
=
"Auto"
/>
</
Grid.ColumnDefinitions
>
<
ContentControl
x:Name
=
"PART_FilterEditorContentControl"
Grid.Column
=
"0"
IsEnabled
=
"{Binding EditorIsEnabled}"
HorizontalContentAlignment
=
"Stretch"
VerticalContentAlignment
=
"Stretch"
Margin
=
"2 1 0 2"
/>
<
telerik:RadDropDownButton
x:Name
=
"PART_DropDownButton"
Grid.Column
=
"1"
KeepOpen
=
"False"
Style
=
"{StaticResource FieldFilterDropDownButtonStyle}"
DropDownIndicatorVisibility
=
"Collapsed"
>
<
Border
Cursor
=
"Hand"
Background
=
"Transparent"
Padding
=
"3 0 0 0"
>
<
Grid
Width
=
"12"
Height
=
"13"
Cursor
=
"Hand"
>
<
Path
Fill
=
"{StaticResource GridView_FilterIconOuterBorder}"
Stretch
=
"Fill"
Margin
=
"0 1 0 0"
Data
=
"M5,9 L6,9 6,10 5,10 z M4,4 L5,4 5,5 5,6 5,7 5,8 5,9 4,9 4,8 4,7 4,6 4,5 z M7.9850001,3 L8.985,3 8.985,4 8,4 8,5 8,6 8,7 8,8 8,9 8,10 8,11 7.9999997,12 6.9999998,12 6.9999998,11 6,11 6,10 6.9999999,10 6.9999999,9 6.9999999,8 6.9999999,7 6.9999999,6 6.9999999,5 6.9999999,4 7.9850001,4
z M3,3 L4,3 4,4 3,4 z M9,2 L10,2 10,3 9,3 z M2,2 L3,2 3,3 2,3 z M7.9999999,0 L9,0 10,0 11,0 12,0 12,1 11,1 11,2 10,2 10,1 9,1 7.9999999,1 z M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6.9999998,0 7.9999997,0 7.9999997,1 6.9999998,1 6,1 5,1 4,1 3,1 2,1 2,2 1,2 1,1 0,1 z"
/>
<
Path
Fill
=
"{StaticResource GridView_FilterIconTopBorder}"
Stretch
=
"Fill"
Margin
=
"2 1 2 0"
VerticalAlignment
=
"Top"
Height
=
"1"
Data
=
"M0,0 L1,0 2,0 3.0000002,0 4.0000002,0 5.0000001,0 5.9850001,0 6.0000001,0 6.9850001,0 7.9850001,0 7.9850001,1 6.9850001,1 6.0000001,1 5.9850001,1 5.0000001,1 4.0000002,1 3.0000002,1 2,1 1,1 0,1 z"
/>
<
Path
Fill
=
"{StaticResource GridView_FilterIconBackground}"
Stretch
=
"Fill"
Margin
=
"3 2 3 3"
Data
=
"M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6,1 5,1 4.985,1 4.985,2 4,2 4,3 4,4 4,5 4,6 4,7 4,8 3,8 3,7 2,7 2,6 2,5 2,4
2,3 2,2 1,2 1,1 0,1 z"
/>
<
Path
Fill
=
"{StaticResource GridView_FilterIconBackground_Filtered}"
Visibility
=
"{Binding FunnelFillVisibility}"
Stretch
=
"Fill"
Margin
=
"3,2,3,3"
Data
=
"M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6,1 5,1 4.985,1 4.985,2 4,2 4,3 4,4 4,5 4,6 4,7 4,8 3,8 3,7 2,7 2,6 2,5 2,4
2,3 2,2 1,2 1,1 0,1 z"
/>
<
Path
Fill
=
"{StaticResource GridView_FilterIconInnerBorder}"
Stretch
=
"Fill"
Margin
=
"0 0 0 1"
Data
=
"M5,9 L6,9 6,10 5,10 z M4,4 L5,4 5,5 5,6 5,7 5,8 5,9 4,9 4,8 4,7 4,6 4,5 z M7.9850001,3 L8.985,3 8.985,4 8,4 8,5 8,6 8,7 8,8 8,9 8,10 8,11 7.9999997,12 6.9999998,12 6.9999998,11 6,11 6,10 6.9999999,10 6.9999999,9 6.9999999,8 6.9999999,7 6.9999999,6 6.9999999,5 6.9999999,4 7.9850001,4
z M3,3 L4,3 4,4 3,4 z M9,2 L10,2 10,3 9,3 z M2,2 L3,2 3,3 2,3 z M7.9999999,0 L9,0 10,0 11,0 12,0 12,1 11,1 11,2 10,2 10,1 9,1 7.9999999,1 z M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6.9999998,0 7.9999997,0 7.9999997,1 6.9999998,1 6,1 5,1 4,1 3,1 2,1 2,2 1,2 1,1 0,1 z"
/>
</
Grid
>
</
Border
>
<
telerik:RadDropDownButton.DropDownContent
>
<
ListBox
x:Name
=
"PART_FilterOperatorsListBox"
ItemsSource
=
"{Binding AvailableOperatorViewModels}"
SelectedItem
=
"{Binding SelectedOperatorViewModel, Mode=TwoWay}"
telerik:StyleManager.Theme
=
"{StaticResource Theme}"
/>
</
telerik:RadDropDownButton.DropDownContent
>
</
telerik:RadDropDownButton
>
</
Grid
>
</
Border
>
</
ControlTemplate
>
Now, the empty ContentControl that you see in yellow called PART_FilterEditorContentControl is the placeholder where the appropriate editor is created and inserted at runtime. The RadDropDownButton in orange is the funnel on the right of it.
You can place a button somewhere, for example between the two, i.e. create another ColumnDefinition.
The DataContext of this FieldFilterControl is a view model called FieldFilterControlViewModel. This view model has a public collection called AvailableOperatorViewModels. This, as you can see above, is the ItemsSource of the ListBox showing the available operators. The first of them is a dummy operator, i.e. it is not an operator, but issues the clear command. As you can see, the SelectedItem of the ListBox is bound to another property on the view model called SelectedOperatorViewModel.
So your task is to simulate the user opening the drop-down and selecting the first choice, which is the pseudo-dummy-operator for Clearing the filters. So when the button is clicked, you can do something like this:
var viewModel = ((FieldFilterControlViewModel)button.DataContext);
viewModel.SelectedOperatorViewModel = viewModel.AvailableOperatorViewModels.First();
This should simulate the use selecting the first item from the list box of available operators, which is the one that issues the clear logic.
The view model has a method called ClearFilter but unfortunately it is private and cannot be used by you. That is why such hacks need to be employed. If you think that you can easily bind your custom clear button to a method on the view model, we can make this method public for the next Latest Internal Build.
Another possible approach for clearing the filter would be to set the Value property of the view model to FilterDescriptor.UnsetValue, which means that the filter is not active, i.e. there is no filter. The Value property is the thing that user enters in the respective editor, i.e. the value that should be filtered with. Setting this to FilterDescriptor.UnsetValue has the same effect as clearing the filter, i.e. the user deleting all the text from the editor.
Note that I have not tried all this I explained above, but it should work. However, this approach involves so many hacks that I cannot recommend it and cannot give any warranties about any side effects it might have. I am posting the source code of the view model for your reference as well:
using
System;
using
System.Collections.Generic;
using
System.Collections.Specialized;
using
System.ComponentModel;
using
System.Diagnostics;
using
System.Linq;
using
System.Windows;
using
Telerik.Windows.Data;
namespace
Telerik.Windows.Controls.GridView
{
/// <summary>
/// This is for internal use only and is not intended to be used directly from your code.
/// </summary>
public
sealed
class
FieldFilterControlViewModel : ViewModelBase
{
internal
const
string
GridViewClearFilter =
"GridViewClearFilter"
;
internal
const
string
GridViewFilterIsTrue =
"GridViewFilterIsTrue"
;
internal
const
string
GridViewFilterIsFalse =
"GridViewFilterIsFalse"
;
private
IFilterableColumn column;
private
OperatorValueFilterDescriptorBase filterDescriptor;
private
readonly
IList<FilterOperatorViewModel> availableOperatorViewModels =
new
List<FilterOperatorViewModel>();
private
FilterOperatorViewModel selectedOperatorViewModel;
private
FilterOperator? defaultOperator;
private
object
valueField = FilterDescriptor.UnsetValue;
private
bool
isCaseSensitive;
private
INotifyCollectionChanged targetFilters;
/// <summary>
/// Gets the available filter operators.
/// </summary>
/// <value>The available filter operators.</value>
public
IEnumerable<FilterOperatorViewModel> AvailableOperatorViewModels
{
get
{
return
this
.availableOperatorViewModels;
}
}
private
void
PopulateAvailableFilterOperatorViewModels()
{
var args =
this
.column.CallFilterOperatorsLoading();
this
.defaultOperator = args.DefaultOperator1;
// This is the "Clear Filter" fake operator.
this
.availableOperatorViewModels.Add(
new
FilterOperatorViewModel(GridViewClearFilter));
// Boolean columns do not have editors, but use fake filter operators.
if
(
this
.IsBooleanFilter)
{
args.AvailableOperators.Remove(FilterOperator.IsEqualTo);
args.AvailableOperators.Remove(FilterOperator.IsNotEqualTo);
this
.availableOperatorViewModels.Add(
new
FilterOperatorViewModel(GridViewFilterIsTrue, FilterOperator.IsEqualTo));
this
.availableOperatorViewModels.Add(
new
FilterOperatorViewModel(GridViewFilterIsFalse, FilterOperator.IsEqualTo));
// This should never change from here on.
this
.defaultOperator = FilterOperator.IsEqualTo;
}
// Finally, yield the "normal", non-fake operators.
foreach
(var filterOperator
in
args.AvailableOperators)
{
this
.availableOperatorViewModels.Add(
new
FilterOperatorViewModel(filterOperator));
}
}
private
bool
IsBooleanFilter
{
get
{
return
this
.column.EffectiveFilteringType ==
typeof
(
bool
) ||
this
.column.EffectiveFilteringType ==
typeof
(
bool
?);
}
}
/// <summary>
/// Gets or sets the selected filter operator.
/// </summary>
/// <value>The selected filter operator.</value>
public
FilterOperatorViewModel SelectedOperatorViewModel
{
get
{
return
this
.selectedOperatorViewModel;
}
set
{
if
(
this
.selectedOperatorViewModel != value)
{
this
.selectedOperatorViewModel = value;
this
.OnSelectedOperatorViewModelChanged();
this
.RefreshEditorIsEnabled();
this
.OnPropertyChanged(
"SelectedOperatorViewModel"
);
}
}
}
private
void
OnSelectedOperatorViewModelChanged()
{
if
(
this
.SelectedOperatorViewModel !=
null
)
{
if
(
this
.SelectedOperatorViewModel.IsReal)
{
// The user has selected a "real" FilterOperator.
this
.ApplyFilter();
}
else
{
switch
(
this
.SelectedOperatorViewModel.LocalizationKey)
{
case
GridViewClearFilter:
// The use has selected the "Clear" dummy filter operator.
this
.ClearFilter();
break
;
case
GridViewFilterIsTrue:
this
.SetFilterDescriptorOperatorToIsEqualToNoCallback();
this
.Value =
true
;
break
;
case
GridViewFilterIsFalse:
this
.SetFilterDescriptorOperatorToIsEqualToNoCallback();
this
.Value =
false
;
break
;
}
}
}
}
private
void
SetFilterDescriptorOperatorToIsEqualToNoCallback()
{
this
.filterDescriptor.PropertyChanged -=
this
.OnFilterDescriptorPropertyChanged;
this
.filterDescriptor.Operator = FilterOperator.IsEqualTo;
this
.filterDescriptor.PropertyChanged +=
this
.OnFilterDescriptorPropertyChanged;
}
private
void
RefreshEditorIsEnabled()
{
FilterOperator? selectedOperator =
this
.SelectedOperatorViewModel.FilterOperator.GetValueOrDefault();
this
.EditorIsEnabled = selectedOperator.HasValue && !selectedOperator.Value.IsUnary();
}
private
bool
editorIsEnabled;
/// <summary>
/// Gets or sets a value indicating whether [editor is enabled].
/// </summary>
/// <value><c>true</c> if [editor is enabled]; otherwise, <c>false</c>.</value>
public
bool
EditorIsEnabled
{
get
{
return
this
.editorIsEnabled;
}
private
set
{
if
(
this
.editorIsEnabled != value)
{
this
.editorIsEnabled = value;
this
.OnPropertyChanged(
"EditorIsEnabled"
);
}
}
}
private
void
SyncSelectedOperatorViewModelFieldWithFilterDescriptorOperator()
{
if
(
this
.IsBooleanFilter)
{
if
(
object
.Equals(
this
.filterDescriptor.Value,
true
))
{
this
.selectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.LocalizationKey == GridViewFilterIsTrue);
}
else
if
(
object
.Equals(
this
.filterDescriptor.Value,
false
))
{
this
.selectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.LocalizationKey == GridViewFilterIsFalse);
}
else
if
(
object
.Equals(
this
.filterDescriptor.Value, OperatorValueFilterDescriptorBase.UnsetValue))
{
this
.selectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.LocalizationKey == GridViewClearFilter);
}
}
else
{
var realSelectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.IsReal && vm.FilterOperator ==
this
.filterDescriptor.Operator);
if
(realSelectedOperatorViewModel !=
null
)
{
this
.selectedOperatorViewModel = realSelectedOperatorViewModel;
}
else
{
this
.selectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.LocalizationKey == GridViewClearFilter);
}
}
this
.OnPropertyChanged(
"SelectedOperatorViewModel"
);
}
/// <summary>
/// Gets or sets the value.
/// </summary>
/// <value>The value.</value>
public
object
Value
{
get
{
return
this
.valueField;
}
set
{
if
(
this
.valueField != value)
{
if
(value == OperatorValueFilterDescriptorBase.UnsetValue)
{
this
.valueField = OperatorValueFilterDescriptorBase.UnsetValue;
}
else
if
(
this
.filterDescriptor.DataType ==
null
)
{
// We do not know the MemberType so we cannot convert anything.
this
.valueField = value;
}
else
if
(FilterDescriptorViewModel.IsNullOrAnEmptyString(value))
{
// TODO: Change the above with null only when we create a way
// to have the StringFilterEditor assign null instead of string.Empty.
if
(
this
.filterDescriptor.DataType.IsValueType &&
!
this
.filterDescriptor.DataType.IsNullableType())
{
this
.valueField = FilterDescriptor.UnsetValue;
}
else
{
this
.valueField =
null
;
}
}
else
if
(value.GetType().IsCompatibleWith(
this
.filterDescriptor.DataType))
{
this
.valueField = value;
}
else
if
(
this
.filterDescriptor.DataType.IsNumericType() && value.GetType().IsNumericType())
{
this
.valueField = value;
}
else
{
object
convertedValue;
var success = TypeExtensions.TryConvert(value,
this
.filterDescriptor.DataType,
out
convertedValue);
if
(success)
{
this
.valueField = convertedValue;
}
else
{
// This will display a validation tooltip which will be localized.
var message = LocalizationManager.GetString(
"FilterEditorFormatExceptionMessage"
);
throw
new
FormatException(message);
}
}
this
.ApplyFilter();
this
.RefreshFunnelFillVisibility();
this
.OnPropertyChanged(
"Value"
);
}
}
}
/// <summary>
/// Gets or sets a value indicating whether this instance is case sensitive.
/// </summary>
/// <value>
/// <c>true</c> if this instance is case sensitive; otherwise, <c>false</c>.
/// </value>
public
bool
IsCaseSensitive
{
get
{
return
this
.isCaseSensitive;
}
set
{
if
(
this
.isCaseSensitive != value)
{
this
.isCaseSensitive = value;
this
.ApplyFilter();
this
.OnPropertyChanged(
"IsCaseSensitive"
);
}
}
}
private
Visibility funnelFillVisibility;
/// <summary>
/// This is for internal use only and is not intended to be used directly from your code.
/// </summary>
public
Visibility FunnelFillVisibility
{
get
{
return
this
.funnelFillVisibility;
}
set
{
if
(
this
.funnelFillVisibility != value)
{
this
.funnelFillVisibility = value;
this
.OnPropertyChanged(
"FunnelFillVisibility"
);
}
}
}
private
void
RefreshFunnelFillVisibility()
{
this
.FunnelFillVisibility =
this
.IsActive ? Visibility.Visible : Visibility.Collapsed;
}
private
bool
IsActive
{
get
{
return
this
.filterDescriptor.IsActive &&
this
.column.TargetFilters.Contains(
this
.column.ColumnFilterDescriptor);
}
}
/// <summary>
/// Initializes a new instance of the <see cref="FieldFilterControlViewModel" /> class. This is for internal use only and is not intended to be used directly from your code.
/// </summary>
internal
FieldFilterControlViewModel(IFilterableColumn column)
{
this
.column = column;
this
.filterDescriptor =
this
.column.ColumnFilterDescriptor.FieldFilter.Filter1;
this
.valueField =
this
.filterDescriptor.Value;
this
.isCaseSensitive =
this
.filterDescriptor.IsCaseSensitive;
this
.PopulateAvailableFilterOperatorViewModels();
this
.InitializeDefaultFilterOperator();
this
.RefreshFunnelFillVisibility();
this
.RefreshEditorIsEnabled();
this
.filterDescriptor.PropertyChanged +=
this
.OnFilterDescriptorPropertyChanged;
this
.targetFilters =
this
.column.TargetFilters;
this
.targetFilters.CollectionChanged +=
this
.OnTargetFiltersCollectionChanged;
}
internal
void
InitializeDefaultFilterOperator()
{
var op =
this
.CalculateDefaultOperator();
if
(op !=
null
)
{
this
.filterDescriptor.Operator = op.Value;
this
.SyncSelectedOperatorViewModelFieldWithFilterDescriptorOperator();
}
else
{
this
.selectedOperatorViewModel =
this
.AvailableOperatorViewModels
.SingleOrDefault(vm => vm.LocalizationKey == GridViewClearFilter);
this
.OnPropertyChanged(
"SelectedOperatorViewModel"
);
}
}
/// <inheritdoc />
protected
override
void
Dispose(
bool
disposing)
{
if
(disposing)
{
if
(
this
.filterDescriptor !=
null
)
{
this
.filterDescriptor.PropertyChanged -=
this
.OnFilterDescriptorPropertyChanged;
this
.filterDescriptor =
null
;
}
if
(
this
.targetFilters !=
null
)
{
this
.targetFilters.CollectionChanged -=
this
.OnTargetFiltersCollectionChanged;
this
.targetFilters =
null
;
}
this
.selectedOperatorViewModel =
null
;
this
.defaultOperator =
null
;
this
.valueField =
null
;
this
.column =
null
;
}
base
.Dispose(disposing);
}
private
void
OnFilterDescriptorPropertyChanged(
object
sender, PropertyChangedEventArgs e)
{
this
.valueField =
this
.filterDescriptor.Value;
this
.OnPropertyChanged(
"Value"
);
this
.isCaseSensitive =
this
.filterDescriptor.IsCaseSensitive;
this
.OnPropertyChanged(
"IsCaseSensitive"
);
this
.SyncSelectedOperatorViewModelFieldWithFilterDescriptorOperator();
this
.RefreshEditorIsEnabled();
this
.RefreshFunnelFillVisibility();
}
private
void
OnTargetFiltersCollectionChanged(
object
sender, NotifyCollectionChangedEventArgs e)
{
this
.RefreshEditorIsEnabled();
this
.RefreshFunnelFillVisibility();
}
private
FilterOperator? CalculateDefaultOperator()
{
if
(
this
.IsActive)
{
return
this
.filterDescriptor.Operator;
}
if
(
this
.defaultOperator.HasValue)
{
return
this
.defaultOperator.Value;
}
return
null
;
}
private
void
ClearFilter()
{
var filtersToRemove =
new
List<IFilterDescriptor>();
if
(
this
.filterDescriptor.IsActive)
{
filtersToRemove.Add(
this
.CreateCopyOfActual());
}
if
(filtersToRemove.Count > 0)
{
if
(
this
.CallFiltering(Enumerable.Empty<IFilterDescriptor>(), filtersToRemove))
{
// Nothing to do since the event was cancelled.
return
;
}
// This will first remove the ColumnFilterDescriptor from TargetFilters
// causing a single CollectionChanged and will then clear it when it is
// no longer part of TargetFilters.
this
.filterDescriptor.PropertyChanged -=
this
.OnFilterDescriptorPropertyChanged;
this
.column.ClearFilters();
this
.filterDescriptor.PropertyChanged +=
this
.OnFilterDescriptorPropertyChanged;
}
Debug.Assert(
this
.filterDescriptor.Value == OperatorValueFilterDescriptorBase.UnsetValue,
"UnsetValue"
);
this
.InitializeDefaultFilterOperator();
this
.RefreshEditorIsEnabled();
this
.Value = OperatorValueFilterDescriptorBase.UnsetValue;
this
.IsCaseSensitive =
false
;
if
(filtersToRemove.Count > 0)
{
this
.CallFiltered(Enumerable.Empty<IFilterDescriptor>(), filtersToRemove);
}
this
.OnPropertyChanged(
"FunnelFillVisibility"
);
}
private
void
ApplyFilter()
{
if
(
this
.SelectedOperatorViewModel ==
null
|| !
this
.SelectedOperatorViewModel.FilterOperator.HasValue)
{
return
;
}
var addedFilters =
new
List<IFilterDescriptor>();
var removedFilters =
new
List<IFilterDescriptor>();
if
(
this
.HasChanges)
{
if
(
this
.filterDescriptor.IsActive)
{
removedFilters.Add(
this
.filterDescriptor.Copy());
}
var actual =
this
.CreateCopyOfActual();
if
(actual.IsActive)
{
addedFilters.Add(actual);
}
}
if
(addedFilters.Count == 0 && removedFilters.Count == 0)
{
// Nothing to do since nothing has changed.
return
;
}
if
(
this
.CallFiltering(addedFilters, removedFilters))
{
this
.RejectChanges();
}
else
{
// Filtering has not been cancelled and has to be applied.
// We want to make the changes in a single batch so we suspend any notifications.
this
.column.ColumnFilterDescriptor.SuspendNotifications();
if
(
this
.HasChanges)
{
this
.AcceptChanges();
}
// Let the grid know that something has changed.
this
.column.ColumnFilterDescriptor.ResumeNotifications();
this
.CallFiltered(addedFilters, removedFilters);
}
}
private
bool
CallFiltering(IEnumerable<IFilterDescriptor> added, IEnumerable<IFilterDescriptor> removed)
{
var args =
this
.column.CallFiltering(added, removed);
return
args.Cancel;
}
private
void
CallFiltered(IEnumerable<IFilterDescriptor> added, IEnumerable<IFilterDescriptor> removed)
{
this
.column.CallFiltered(added, removed);
}
internal
bool
HasChanges
{
get
{
return
this
.filterDescriptor.Operator !=
this
.SelectedOperatorViewModel.FilterOperator.GetValueOrDefault() ||
this
.filterDescriptor.Value !=
this
.Value ||
this
.filterDescriptor.IsCaseSensitive !=
this
.IsCaseSensitive;
}
}
internal
OperatorValueFilterDescriptorBase CreateCopyOfActual()
{
var copyOfActual =
this
.filterDescriptor.Copy();
if
(
this
.SelectedOperatorViewModel.IsReal)
{
copyOfActual.Operator =
this
.SelectedOperatorViewModel.FilterOperator.Value;
}
copyOfActual.Value =
this
.Value;
copyOfActual.IsCaseSensitive =
this
.IsCaseSensitive;
return
copyOfActual;
}
internal
void
AcceptChanges()
{
this
.filterDescriptor.PropertyChanged -=
this
.OnFilterDescriptorPropertyChanged;
this
.filterDescriptor.Operator =
this
.SelectedOperatorViewModel.FilterOperator.Value;
this
.filterDescriptor.Value =
this
.Value;
this
.filterDescriptor.IsCaseSensitive =
this
.IsCaseSensitive;
this
.filterDescriptor.PropertyChanged +=
this
.OnFilterDescriptorPropertyChanged;
}
internal
void
RejectChanges()
{
this
.SyncSelectedOperatorViewModelFieldWithFilterDescriptorOperator();
this
.Value =
this
.filterDescriptor.Value;
this
.IsCaseSensitive =
this
.filterDescriptor.IsCaseSensitive;
}
}
}
I hope that this information can give you a start in exploring the different options that you have and experiment.
Regards,
Rossen Hristov
Telerik
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.
Great I gave it a try and it works fine so far. Thanks!!
As you indicated I created a third column and added a Button. I created a TriggerAction class to do the actuall clearing and from my Style file using an interaction trigger I Trigger on the click event.
Here is the snippet :
<ControlTemplate x:Key="FieldFilterControlTemplate" TargetType="telerik:FieldFilterControl">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="PART_MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<ContentControl x:Name="PART_FilterEditorContentControl" Grid.Column="0" HorizontalContentAlignment="Stretch" IsEnabled="{Binding EditorIsEnabled}" Margin="2,1,0,2" VerticalContentAlignment="Stretch" Style="{StaticResource ContentControlStyleFilter}"/>
<Button Content="Clear" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" >
<local:GridFilterClearAction />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<telerik:RadDropDownButton x:Name="PART_DropDownButton" Grid.Column="2" DropDownIndicatorVisibility="Collapsed" KeepOpen="False" Style="{StaticResource FieldFilterDropDownButtonStyle}" >
<telerik:RadDropDownButton.DropDownContent>
<ListBox x:Name="PART_FilterOperatorsListBox" ItemsSource="{Binding AvailableOperatorViewModels}" SelectedItem="{Binding SelectedOperatorViewModel, Mode=TwoWay}" Style="{StaticResource ListBoxStyleGridFilter}" />
</telerik:RadDropDownButton.DropDownContent>
<Border Background="Transparent" Cursor="Hand" Padding="0,0,1,0">
<Grid Cursor="Hand" Height="13" Width="12">
<Path Data="M5,9 L6,9 6,10 5,10 z M4,4 L5,4 5,5 5,6 5,7 5,8 5,9 4,9 4,8 4,7 4,6 4,5 z M7.9850001,3 L8.985,3 8.985,4 8,4 8,5 8,6 8,7 8,8 8,9 8,10 8,11 7.9999997,12 6.9999998,12 6.9999998,11 6,11 6,10 6.9999999,10 6.9999999,9 6.9999999,8 6.9999999,7 6.9999999,6 6.9999999,5 6.9999999,4 7.9850001,4
 z M3,3 L4,3 4,4 3,4 z M9,2 L10,2 10,3 9,3 z M2,2 L3,2 3,3 2,3 z M7.9999999,0 L9,0 10,0 11,0 12,0 12,1 11,1 11,2 10,2 10,1 9,1 7.9999999,1 z M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6.9999998,0 7.9999997,0 7.9999997,1 6.9999998,1 6,1 5,1 4,1 3,1 2,1 2,2 1,2 1,1 0,1 z" Fill="{StaticResource GridView_FilterIconOuterBorder}" Margin="0,1,0,0" Stretch="Fill"/>
<Path Data="M0,0 L1,0 2,0 3.0000002,0 4.0000002,0 5.0000001,0 5.9850001,0 6.0000001,0 6.9850001,0 7.9850001,0 7.9850001,1 6.9850001,1 6.0000001,1 5.9850001,1 5.0000001,1 4.0000002,1 3.0000002,1 2,1 1,1 0,1 z" Fill="{StaticResource GridView_FilterIconTopBorder}" Height="1" Margin="2,1,2,0" Stretch="Fill" VerticalAlignment="Top"/>
<Path Data="M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6,1 5,1 4.985,1 4.985,2 4,2 4,3 4,4 4,5 4,6 4,7 4,8 3,8 3,7 2,7 2,6 2,5 2,4
2,3 2,2 1,2 1,1 0,1 z" Fill="{StaticResource GridView_FilterIconBackground}" Margin="3,2,3,3" Stretch="Fill"/>
<Path Data="M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6,1 5,1 4.985,1 4.985,2 4,2 4,3 4,4 4,5 4,6 4,7 4,8 3,8 3,7 2,7 2,6 2,5 2,4
2,3 2,2 1,2 1,1 0,1 z" Fill="{StaticResource GridView_FilterIconBackground_Filtered}" Margin="3,2,3,3" Stretch="Fill" Visibility="{Binding FunnelFillVisibility}"/>
<Path Data="M5,9 L6,9 6,10 5,10 z M4,4 L5,4 5,5 5,6 5,7 5,8 5,9 4,9 4,8 4,7 4,6 4,5 z M7.9850001,3 L8.985,3 8.985,4 8,4 8,5 8,6 8,7 8,8 8,9 8,10 8,11 7.9999997,12 6.9999998,12 6.9999998,11 6,11 6,10 6.9999999,10 6.9999999,9 6.9999999,8 6.9999999,7 6.9999999,6 6.9999999,5 6.9999999,4 7.9850001,4
 z M3,3 L4,3 4,4 3,4 z M9,2 L10,2 10,3 9,3 z M2,2 L3,2 3,3 2,3 z M7.9999999,0 L9,0 10,0 11,0 12,0 12,1 11,1 11,2 10,2 10,1 9,1 7.9999999,1 z M0,0 L1,0 2,0 3,0 4,0 5,0 6,0 6.9999998,0 7.9999997,0 7.9999997,1 6.9999998,1 6,1 5,1 4,1 3,1 2,1 2,2 1,2 1,1 0,1 z" Fill="{StaticResource GridView_FilterIconInnerBorder}" Margin="0,0,0,1" Stretch="Fill"/>
</Grid>
</Border>
</telerik:RadDropDownButton>
</Grid>
</Border>
</ControlTemplate>
-----TriggerAction Class ----
using System.Linq;
using System.Windows.Controls;
using System.Windows.Interactivity;
using Telerik.Windows.Controls.GridView;
namespace somenamespace
{
public class GridFilterClearAction : TriggerAction<Button>
{
protected override void Invoke(object parameter)
{
if (AssociatedObject == null)
return;
if (AssociatedObject is Button)
{
var button = AssociatedObject;
var viewModel = ((FieldFilterControlViewModel)button.DataContext);
viewModel.SelectedOperatorViewModel = viewModel.AvailableOperatorViewModels.First();
}
}
}
}
Seems to me that the ItemSources in the ListBox below should already contain
the operators with the icons.
<ListBox x:Name="PART_FilterOperatorsListBox" ItemsSource="{Binding AvailableOperatorViewModels}" SelectedItem="{Binding SelectedOperatorViewModel, Mode=TwoWay}" Style="{StaticResource ListBoxStyleGridFilter}" />
I hoped to do this by adding new operators OnFilterOperatorsLoading with icons from code into the AvailableOperators but that is a RemoveOnlyCollection.
Let me know if this is somehow achivable.
Thanks!
I am afraid that we do not have any icons for the operators and we never had ones. You might need to define a custom ItemTemplate for the ListBox and provide operator icons of your own.
Regards,Rossen Hristov
Telerik
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.
1 - Isn't an item template templating one single Item? if so how can I use it to template the 15 available items?
-- My goal is to have an icon per operator
2 - Even if I could somehow do this What are the Binding Properties of the Operators?
3 - Would templating an item somehow also template the Selected Item? Because my goal is when user selects an item I would like to replace the Funnel dropdown icon with the selected operator icon, to give user a visual cue.
Thanks for your help.
<ListBox x:Name="PART_FilterOperatorsListBox" ItemsSource="{Binding AvailableOperatorViewModels}" SelectedItem="{Binding SelectedOperatorViewModel, Mode=TwoWay}" Style="{StaticResource ListBoxStyleGridFilter}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse Fill="Aqua" Width="10" Height="7"></Ellipse>
<TextBlock Text="{Binding ???}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
1) Please, read this article before going on.
You can create an item template which contains your SL image. The data context of each list box item will be the respective FilterOperatorViewModel instance. The FilterOperatorViewModel has a property called FilterOperator which returns the actual FilterOperator enum value.
Now, you can data bind the source of your SL image to the FilterOperator property with an IValueConverter. This IValueConverter will accept the FilterOperator enum value and return the respective icon or path to the image or you get the idea. This is up to you. This topic however goes way beyond the scope of Telerik support since this is general Silverlight know-how, i.e. data binding, images, converters and so on.
Something like this in pseudo-code:
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding FilterOperator, Converter=<<your converter that accepts a filter operator and return an image or path>>}"/>
</DataTemplate>
</ListBox.ItemTemplate>
Here is the source code of the FilterOperatorViewModel class:
using
Telerik.Windows.Data;
namespace
Telerik.Windows.Controls.GridView
{
/// <summary>
/// This is for internal use only and is not intended to be used directly from your code.
/// </summary>
public
sealed
class
FilterOperatorViewModel
{
private
readonly
FilterOperator? filterOperator =
null
;
internal
FilterOperatorViewModel(
string
localizationKey)
{
this
.LocalizationKey = localizationKey;
}
internal
FilterOperatorViewModel(
string
localizationKey, FilterOperator filterOperator)
{
// This constructor is for IsTrue and IsNotTrue
this
.LocalizationKey = localizationKey;
this
.filterOperator = filterOperator;
}
internal
FilterOperatorViewModel(FilterOperator filterOperator)
{
this
.IsReal =
true
;
this
.LocalizationKey =
string
.Format(
"GridViewFilter{0}"
, filterOperator.ToString());
this
.filterOperator = filterOperator;
}
internal
bool
IsReal {
get
;
private
set
; }
internal
string
LocalizationKey {
get
;
private
set
; }
/// <summary>
/// Gets the filter operator.
/// </summary>
/// <value>The filter operator.</value>
public
FilterOperator? FilterOperator
{
get
{
return
this
.filterOperator;
}
}
/// <inheritdoc />
public
override
string
ToString()
{
return
LocalizationManager.GetString(
this
.LocalizationKey);
}
}
}
2) Simply bind the thing in your item template to the FilterOperator property. Remember that the DataContext of the thing in the item template is an instance of the FilterOperatorViewModel class, which has a single property called FilterOperator.
3) I really don't think that this is possible. The funnel that you see is the Path inside the PART_DropDownButton. I don't think that you can easily change that. Let us know if you manage to invent a way to do that.
Please, note that I have provided all the default XAML that is relevant and all relevant view model classes, which by the way you can easily find in the source code yourself. How you customize all of this XAML from here on would really be up to you and really falls beyond the scope of Telerik-specific support. If you are having difficulties with this process, you can always count on our Enterprise Services.
I hope this helps. Regards,
Rossen Hristov
Telerik
Explore the entire Telerik portfolio by downloading Telerik DevCraft Ultimate.