Tuesday 15 May 2012

Windows 8 Metro - GridView ItemClick Command Attached Property

Windows 8 Metro doesn't support triggers, so it's not easy to bind commands to events in the view any more. This is an attached property which can be attached to a GridView to bind a command to the ItemClick event:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;


namespace AAWP.Commanding
{
    public class GridViewItemClickCommand
    {
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(GridViewItemClickCommand), new PropertyMetadata(null, CommandPropertyChanged));


        public static void SetCommand(DependencyObject attached, ICommand value)
        {
            attached.SetValue(CommandProperty, value);
        }


        public static ICommand GetCommand(DependencyObject attached)
        {
            return (ICommand)attached.GetValue(CommandProperty);
        }


        private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Attach click handler
            (d as GridView).ItemClick += gridView_ItemClick;
        } 


        private static void gridView_ItemClick(object sender, ItemClickEventArgs e)
        {
            // Get GridView
            var gridView = (sender as GridView);


            // Get command
            ICommand command = GetCommand(gridView);


            // Execute command
            command.Execute(e.ClickedItem);
        }
    }
}

Usage:

<GridView
                x:Name="itemGridView"
                AutomationProperties.AutomationId="ItemGridView"
                AutomationProperties.Name="Grouped Items"
                Margin="116,0,40,46"
                ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
                ItemTemplate="{StaticResource Standard250x250ItemTemplate}"
                SelectionMode="None"
                IsItemClickEnabled="True"
                cmd:GridViewItemClickCommand.Command="{Binding ItemClickCommand}" >

16 comments:

  1. First of all thank you this is a much needed assist given all the MS examples fail to comply with many aspects of MVVM. However after copying this code exactly all I get in my XAML is an error The name "GridViewItemClickCommand" does not exist in the namespace "using:AAWP.Commanding"

    Any idea what I am failing to do. I tried rebuilding but to no avail

    ReplyDelete
  2. Have you used the same namespace or put it in your own? If it's in your own NS use that in the XAML. If it's not, try using your own NS which matches the assembly

    ReplyDelete
  3. I did both, Initially I put the file in common and renamed the namespace and got the error so I deleted it redid it keeping the namespace and got the same error. I am using the LayoutAwarePage if that makes any difference rather than the regular page.

    ReplyDelete
  4. So you changed your xaml to using:yournamespace.subnamespace ?

    ReplyDelete
  5. Yes in my case I was already using common for other things in the xaml and I had changed the namespace on this file to ProjectName.Common so I just called common: which referenced that namespace. It works for everything else I call. Just one of those weird XAML things that will prove to be simple I am sure but which will take about 38 hours to solve. :)

    ReplyDelete
  6. Just a quick q, does it build? My xaml is always full of errors at design time on win8 :-/

    ReplyDelete
  7. Yes it builds just fine I added an RTFTextBlock Convertor today and I have the same problem with that one. Project still builds though

    ReplyDelete
  8. If it builds and runs, don't worry about it :-)

    ReplyDelete
  9. So I gave this a whirl and am not getting any results. I added breakpoints inside of every method and none of them are getting hit.

    It compiles great, I can add the dependency property to my .xaml page, but nothing gets fired.

    Any ideas?


    public Command AlmDataItemClickedCommand { get; set; }
    public GroupDetailPageViewModel()
    {
    AlmDataItemClickedCommand = new Command(itemClicked);
    }


    I copy/pasted your class and changed the name of it (as you can see).

    ReplyDelete
  10. You need to bind to something in the vm which implements the ICommand interface like a relay command or delegate command. http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

    Cheers

    ReplyDelete
  11. Worked like a charm. Thanks a bunch!

    ReplyDelete
  12. Hi, first of all thank you a lot for this solution.

    I have a problem with parameters of selected item. I'm using a RelayCommand implementation in viewmodel.

    private ICommand _itemClick;

    _itemClick = new RelayCommand(ItemClickHandler);

    private void ItemClickHandler()
    {
    //need slecteditem
    }

    How i can pass parameters to handler?

    Thanks!

    ReplyDelete
  13. thanks.
    event is work greatly
    but i can't get/set selecteditem from the binding.
    i tried set my selecteditem in relay command.
    but nothing happend.

    how can i set binding selecteditem?

    ReplyDelete
  14. thanks.
    event works greatly.
    but i can't get/set my selecteditem.
    i tried to get/set selecteditem's value in relay command.
    but nothing happened.
    how can i get/set selecteditem in viewmodel?

    ReplyDelete
  15. Just a note, I struggled for a while getting the command code to be reached - eventually I realized that I was referencing a command in the view model that had been set as my ItemSource for the GridView. When I referenced a command one level up, it worked.

    Hope this helps..

    ReplyDelete