I have a GridView with GridViewColumn, the header uses a template with a textblox to show the column name. I want a command attached to this column, basically when the user clicks the column, a command in my VM g开发者_运维技巧ets called ?
this is for sorting purposes.
Thanks
Probably too late to help you but for anyone else looking for an answer, you can do this using Attached Behaviours and the GridViewColumnHeader.Click event (see this MSDN article on sorting a GridView on header item click).
My code was as follows; XAML:
<ListView Width="Auto" Height="Auto" Margin="12,12,12,12"
ItemsSource="{Binding SearchResults}"
behav:GridViewColumnHeaderClick.Command="{Binding SortViewCommand}">
(where 'behav' is the namespace for my attached behaviours). The attached behaviour classes look like this:
public class GridViewColumnHeaderClick
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(GridViewColumnHeaderClick), new UIPropertyMetadata(null,
GridViewColumnHeaderClick.CommandChanged));
public static readonly DependencyProperty CommandBehaviourProperty =
DependencyProperty.RegisterAttached("CommandBehaviour", typeof(GridViewColumnHeaderClickCommandBehaviour), typeof(GridViewColumnHeaderClick),
new UIPropertyMetadata(null));
public static ICommand GetCommand(DependencyObject obj)
{
return (ICommand)obj.GetValue(CommandProperty);
}
public static void SetCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(CommandProperty, value);
}
public static GridViewColumnHeaderClickCommandBehaviour GetCommandBehaviour(DependencyObject obj)
{
return (GridViewColumnHeaderClickCommandBehaviour)obj.GetValue(CommandBehaviourProperty);
}
public static void SetCommandBehaviour(DependencyObject obj, GridViewColumnHeaderClickCommandBehaviour value)
{
obj.SetValue(CommandBehaviourProperty, value);
}
private static void CommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
GridViewColumnHeaderClick.GetOrCreateBehaviour(sender).Command = e.NewValue as ICommand;
}
private static GridViewColumnHeaderClickCommandBehaviour GetOrCreateBehaviour(DependencyObject element)
{
GridViewColumnHeaderClickCommandBehaviour returnVal = GridViewColumnHeaderClick.GetCommandBehaviour(element);
if (returnVal == null)
{
ListView typedElement = element as ListView;
if (typedElement == null)
{
throw new InvalidOperationException("GridViewColumnHeaderClick.Command property can only be set on instances of ListView");
}
returnVal = new GridViewColumnHeaderClickCommandBehaviour(typedElement);
GridViewColumnHeaderClick.SetCommandBehaviour(element, returnVal);
}
return returnVal;
}
}
and:
public class GridViewColumnHeaderClickCommandBehaviour
{
public GridViewColumnHeaderClickCommandBehaviour(ListView element)
{
element.AddHandler(GridViewColumnHeader.ClickEvent, new RoutedEventHandler(this.ClickEventHandler));
}
public ICommand Command { get; set; }
private void ClickEventHandler(object sender, RoutedEventArgs e)
{
ICommand localCommand = this.Command;
object parameter = e.OriginalSource as GridViewColumnHeader;
if ((localCommand != null) && localCommand.CanExecute(parameter))
{
localCommand.Execute(parameter);
}
}
}
and then your command can be based on the event handler described in the MSDN article:
private void SortResults(string sortBy, ListSortDirection direction)
{
ICollectionView dataView = CollectionViewSource.GetDefaultView(this.SearchResults); // where SearchResults is the data to which the ListView is bound
dataView.SortDescriptions.Clear();
SortDescription sortDescription = new SortDescription(sortBy, direction);
dataView.SortDescriptions.Add(sortDescription);
dataView.Refresh();
}
private void SortViewCommandHandler(object parameter)
{
GridViewColumnHeader typedParameter = parameter as GridViewColumnHeader;
ListSortDirection direction;
if (typedParameter != null)
{
if (typedParameter.Role != GridViewColumnHeaderRole.Padding)
{
if (typedParameter != this.previousSortHeader)
{
direction = ListSortDirection.Ascending;
}
else
{
if (this.previousSortDirection == ListSortDirection.Ascending)
{
direction = ListSortDirection.Descending;
}
else
{
direction = ListSortDirection.Ascending;
}
}
string headerLabel = typedParameter.Column.Header as string;
this.SortResults(headerLabel, direction);
if (direction == ListSortDirection.Ascending)
{
typedParameter.Column.HeaderTemplate =
Resources["HeaderTemplateArrowUp"] as DataTemplate;
}
else
{
typedParameter.Column.HeaderTemplate =
Resources["HeaderTemplateArrowDown"] as DataTemplate;
}
// Remove arrow from previously sorted header
if ((this.previousSortHeader != null) && (this.previousSortHeader != typedParameter))
{
this.previousSortHeader.Column.HeaderTemplate = null;
}
this.previousSortHeader = typedParameter;
this.previousSortDirection = direction;
}
}
}
I haven't yet thought of an MVVM-ish way to set the header template (the view should obviously be bound to something in here) so you're on your own there!
Note that I'm deviating slightly from the Josh Smith article in my implementation of the attached behaviour - having a separate class makes multiple stateful handlers easier than using a static event handler method, so it's a pattern I follow in general.
You can replace the textblock in the header template with a button and then attach a command to it. If you want, you can set a style for the buttons to remove the borders.
精彩评论