问题
How to Set Filter Icon in the DataGrid, it should be Visible on MouseOver and Filter Popup IsOpen otherwise Collapsed.
Note:
Design the Datagrid in one XAML and the Filter Popup should be in Main View XAML, The DataGrid XAML inherits the Main View XAML.
The Filter Icon is a Button designed in app.xaml and call the Style in the DataGrid.
I need to show (Visibility:Visible
) the Filter Icon Button while on Mouse Hover Event of the Corresponding Column or If user can click the Filter Icon Button then the Filter Popup gets opened and that time the Icon should show - Filter Popup IsOpen=True
state. The Filter Icon Button should Collapsed while on Popup gets closed while on Icon Button Clicked otherwise Mouse Hover is not happened.
Main View XAML : Popup Source Code
<Popup Name="popFilter" Placement="Mouse" StaysOpen="False" Width="200" IsOpen="{Binding IsPopupFilterOpen, Mode=TwoWay}">
<Border Background="White" BorderBrush="Gray" BorderThickness="1,1,1,1">
<StackPanel Margin="5,5,5,15">
<TextBlock Text="Welcome to Popup Window" />
</StackPanel>
</Border>
</Popup>
Child View XAML : DataGrid
<DataGrid Name="EmpList" ColumnHeaderStyle="{StaticResource FilterDataGridColumnHeader}" AutoGenerateColumns="False" ItemsSource="{Binding EmpList}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Header="Employee Name" ElementStyle="{StaticResource DataGridElementStyle}" />
<DataGridTextColumn Binding="{Binding Age}" Header="Employee Age" ElementStyle="{StaticResource DataGridElementStyle}" />
</DataGrid.Columns>
</DataGrid>
Filter Icon Button : App.xaml
<Application.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}" x:Key="FilterDataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Button Grid.Column="2" Command="{Binding Path=DataContext.PopUpCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Style="{StaticResource TransButtonStyleKey}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiValueConverterKey}">
<Binding RelativeSource="{ RelativeSource Mode=FindAncestor, AncestorType={x:Type cust:DataGrid}}" />
<Binding Path="Column" RelativeSource="{ RelativeSource Mode=TemplatedParent}" />
</MultiBinding>
</Button.CommandParameter>
<ContentControl Name="autofilter" Visibility="Collapsed" ContentTemplate="{StaticResource FilterButtonStyleKey}" Margin="0 0 3 0"></ContentControl>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
FilterButtonStyleKey :
<DataTemplate x:Key="FilterButtonStyleKey">
<Canvas Height="15.898" Width="15.297" HorizontalAlignment="Center" VerticalAlignment="Center" Background="Transparent">
<Path Data="M16.0117,6.7368C18.3417,6.7368,20.6727,6.7358,23.0027,6.7378C23.5327,6.7378,23.5977,6.8308,23.6437,7.3438C23.7027,7.9958,23.4897,8.4748,23.0197,8.9548C21.4107,10.5968,19.8547,12.2888,18.2957,13.9788C18.1647,14.1208,18.1137,14.3828,18.1117,14.5898C18.0987,17.0608,18.1067,19.5308,18.0907,22.0018C18.0887,22.2158,18.0077,22.4968,17.8607,22.6158C17.7697,22.6878,17.4587,22.5408,17.2807,22.4368C16.3057,21.8718,15.3447,21.2788,14.3677,20.7148C14.0637,20.5408,13.9287,20.3278,13.9297,19.9728C13.9407,18.1778,13.9257,16.3848,13.9357,14.5908C13.9367,14.2698,13.8367,14.0388,13.6137,13.8058C12.1347,12.2548,10.6717,10.6898,9.2027,9.1298C9.0967,9.0168,8.9927,8.9018,8.8797,8.7958C8.4137,8.3608,8.2387,7.6118,8.4377,7.0158C8.5277,6.7478,8.7137,6.7358,8.9347,6.7368C10.0937,6.7388,11.2517,6.7378,12.4097,6.7378C13.6107,6.7378,14.8107,6.7378,16.0117,6.7368z" Height="16.898" Canvas.Left="-0.5" StrokeStartLineCap="Round" Stretch="Fill" StrokeEndLineCap="Round" Stroke="#FF323232" StrokeThickness="1" StrokeLineJoin="Round" Canvas.Top="-0.5" Width="16.297"/>
<Path Data="M14.2427,14.3921L17.9117,14.3921" Height="1" Canvas.Left="5.386" StrokeStartLineCap="Round" Stretch="Fill" StrokeEndLineCap="Round" Stroke="#FF323232" StrokeThickness="1" StrokeLineJoin="Round" Canvas.Top="7.156" Width="4.669"/>
</Canvas>
</DataTemplate>
回答1:
**Solving the entire problem using custom behaviors **
Create a new sub-classed button DataGridColHeaderButton. We need this class for 2 main reasons : a) To distinguish between normal button and column-header button. b) To have access to the Popup to show it.
Create 2 separate behaviors viz; ShowPopupBehavior, ShowHideFilterIconBehavior.
Introduce a new CLR/DP property PopupFilter in the ViewModel. As Popup is present in our ViewModel, and we want this Popup to reach our special buttons which are present in Style. This we will do using binding.
public MainWindow() { InitializeComponent(); /* new clr property */ PopupFilter = popFilter; this.DataContext = this; }
Our style looks like below. Notice the binding, and replace Button with DataGridColHeaderButton in original style.
<Style x:Key="FilterDataGridColumnHeader" ...>
...
<local:DataGridColHeaderButton PopupToShow="{Binding PopupFilter, RelativeSource={RelativeSource AncestorType=Window, Mode=FindAncestor}}">
<local:DataGridColHeaderButton.Content>
...
</local:DataGridColHeaderButton.Content>
</local:DataGridColHeaderButton>
...
</Style>
DataGridColHeaderButton.cs
public class DataGridColHeaderButton : Button
{
// Using a DependencyProperty as the backing store for PopupToShow. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PopupToShowProperty =
DependencyProperty.Register("PopupToShow", typeof(Popup), typeof(DataGridColHeaderButton), new PropertyMetadata(null));
public Popup PopupToShow
{
get { return (Popup)GetValue(PopupToShowProperty); }
set { SetValue(PopupToShowProperty, value); }
}
public ShowPopupBehavior popupBehavior { get; set; }
public ShowHideFilterIconBehavior showHideBehavior { get; set; }
public DataGridColHeaderButton()
{
popupBehavior = new ShowPopupBehavior(this);
showHideBehavior = new ShowHideFilterIconBehavior(this);
}
}
ShowPopupBehavior.cs
public class ShowPopupBehavior:Behavior<DataGridColHeaderButton>
{
Popup popup;
public ShowPopupBehavior(DataGridColHeaderButton btn)
{
this.Attach(btn);
}
protected override void OnAttached()
{
AssociatedObject.Loaded += AssociatedObject_Loaded;
AssociatedObject.Click += AssociatedObject_Click;
base.OnAttached();
}
void AssociatedObject_Click(object sender, RoutedEventArgs e)
{
AssociatedObject.PopupToShow.IsOpen = true;
ContentControl autofilter = (ContentControl)VisualTreeHelper.GetChild(((Grid)(AssociatedObject.Content)), 1);
autofilter.Visibility = Visibility.Visible;
popup.PlacementTarget = AssociatedObject;
}
void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
popup = AssociatedObject.PopupToShow;
popup.Closed += popup_Closed;
}
void popup_Closed(object sender, EventArgs e)
{
ContentControl autofilter = (ContentControl)VisualTreeHelper.GetChild(((Grid)(AssociatedObject.Content)), 1);
autofilter.Visibility = Visibility.Collapsed;
}
}
ShowHideFilterIconBehavior.cs
public class ShowHideFilterIconBehavior : Behavior<DataGridColHeaderButton>
{
public ShowHideFilterIconBehavior(DataGridColHeaderButton btn)
{
this.Attach(btn);
}
protected override void OnAttached()
{
AssociatedObject.MouseEnter += AssociatedObject_MouseEnter;
AssociatedObject.MouseLeave += AssociatedObject_MouseLeave;
base.OnAttached();
}
void AssociatedObject_MouseLeave(object sender, MouseEventArgs e)
{
ContentControl autofilter = (ContentControl)VisualTreeHelper.GetChild(((Grid)(AssociatedObject.Content)), 1);
autofilter.Visibility = Visibility.Collapsed;
}
void AssociatedObject_MouseEnter(object sender, MouseEventArgs e)
{
ContentControl autofilter = (ContentControl)VisualTreeHelper.GetChild(((Grid)(AssociatedObject.Content)), 1);
autofilter.Visibility = Visibility.Visible;
}
}
** Using the Behaviors in XAML **
Now we can also attach these behaviors like we do with standard ones.
For that, we need parameterless(default) constructors in our behaviors.
Then, we don't need behavior related properties in our DataGridColHeaderButton like we have to do while attaching these behaviors in code.
Attaching these behaviors in XAML means we can comment them out anytime.
<local:DataGridColHeaderButton ...> <i:Interaction.Behaviors> <local:ShowHideFilterIconBehavior /> <local:ShowPopupBehavior /> </i:Interaction.Behaviors> ... </local:DataGridColHeaderButton>
回答2:
In this I named the Button as x:Name="autoFilterBtn"
I added one more Command Parameter <Binding RelativeSource="{RelativeSource Mode=Self}"/>
within the Button Command.
Filter Icon Button : App.xaml
<Application.Resources>
<Style TargetType="{x:Type DataGridColumnHeader}" x:Key="FilterDataGridColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
<Button x:Name="autoFilterBtn" Grid.Column="2" Command="{Binding Path=DataContext.PopUpCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}" Style="{StaticResource TransButtonStyleKey}">
<Button.CommandParameter>
<MultiBinding Converter="{StaticResource MultiValueConverterKey}">
<Binding RelativeSource="{ RelativeSource Mode=FindAncestor, AncestorType={x:Type cust:DataGrid}}" />
<Binding Path="Column" RelativeSource="{ RelativeSource Mode=TemplatedParent}" />
<Binding RelativeSource="{RelativeSource Mode=Self}"/>
</MultiBinding>
</Button.CommandParameter>
<ContentControl Name="autofilter" Visibility="Collapsed" ContentTemplate="{StaticResource FilterButtonStyleKey}" Margin="0 0 3 0"></ContentControl>
</Button>
<ControlTemplate.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<MultiBinding Converter="{StaticResource FilterVisibilityCheckConverter}">
<Binding Path="IsMouseOver" RelativeSource="{RelativeSource Self}" />
<Binding Path="DataContext.FilterButtonHashCode" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}" />
<Binding ElementName="autoFilterBtn" />
</MultiBinding>
</DataTrigger.Binding>
<Setter TargetName="autofilter" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
At the time of PopUpCommand
Command execution I updated the int Property FilterButtonHashCode
private Button _filterButton = new Button();
public Button FilterButton
{
get { return _filterButton; }
set
{
_filterButton = value;
OnPropertyChanged();
}
}
private int _filterButtonHashCode = 0;
public int FilterButtonHashCode
{
get { return _filterButtonHashCode; }
set
{
_filterButtonHashCode = value;
OnPropertyChanged();
}
}
private bool _isPopupFilterOpen = false;
public bool IsPopupFilterOpen
{
get { return _isPopupFilterOpen; }
set
{
_isPopupFilterOpen = value;
if (value == false)
FilterButtonHashCode = 0;
OnPropertyChanged();
}
}
public ICommand FilterPopUpCommand
{
get
{
return new DelegatingCommand((param) =>
{
object[] obj = param as object[];
FilterButton = obj[2] as Button;
FilterButtonHashCode = FilterButton.GetHashCode();
// Logic to Show Filter Popup
});
}
}
MultiValue Converter Class:
public class FilterVisibilityCheckConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
bool isMouseHover = false;
int hasCode = 0;
Button filterButton = new Button();
if ((values[0] ?? false) is bool)
{
isMouseHover = bool.Parse((values[0] ?? false).ToString());
}
if (values[1] is int)
{
hasCode = int.Parse((values[1] ?? "0").ToString());
}
if (values[2] is Button)
{
filterButton = values[2] as Button;
}
if (values != null && values.Length == 3 && values[0] != null && values[1] != null && values[2] != null && ((isMouseHover == true) || (hasCode == filterButton.GetHashCode())))
{
return true;
}
else
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
This Converter returns true IsMouseHover
is True
OR the value of the FilterButtonHashCode
property matches with the Button.GetHashCode()
. Based on this simple Boolean return Multivalue Converter with Multiple Converter Parameter, we can achieve the above task without C# Behaviors
来源:https://stackoverflow.com/questions/34327280/how-to-write-datatrigger-to-visible-the-filter-icon-in-the-datagrid-while-on-mou