views:

21

answers:

2

Hello.
A window has a MenuItem binded to a collection, the MenuItem has ItemTemplate, which contains another MenuItem with an attached property using binding:

<Menu Background="Transparent">
        <MenuItem ItemsSource="{Binding SomeThings}" Header="Menu">
            <MenuItem.ItemTemplate>
                <DataTemplate>
                    <MenuItem Header="{Binding MenuTitle}" WpfApplication30:MainWindow.KeyGesture="{Binding KeyGesture}"/>
                </DataTemplate>
            </MenuItem.ItemTemplate>
        </MenuItem>
    </Menu>

The declaration of datasource and attached property:

private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        DataContext = new { 
            SomeThings = new[]
                {
                    new { MenuTitle = "Title 1", KeyGesture = new KeyGesture(Key.A, ModifierKeys.Control) }, 
                    new { MenuTitle = "Title 2", KeyGesture = new KeyGesture(Key.B, ModifierKeys.Control) }, 
                    new { MenuTitle = "Title 3", KeyGesture = new KeyGesture(Key.C, ModifierKeys.Control) }, 
                } };
    }

    public static string GetKeyGesture(MenuItem obj)
    {
        return (string)obj.GetValue(KeyGestureProperty);
    }

    public static void SetKeyGesture(MenuItem obj, string value)
    {
        obj.SetValue(KeyGestureProperty, value);
    }

    public static readonly DependencyProperty KeyGestureProperty =
        DependencyProperty.RegisterAttached("KeyGesture", typeof(KeyGesture), typeof(MainWindow), 
            new PropertyMetadata(OnSetKeyGestureCallback));

    private static void OnSetKeyGestureCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
    {
        var window = GetWindow(dependencyObject);
        var keyGesture = (KeyGesture)e.NewValue;
        window.KeyDown += (sender, keyArgs) =>
        {
            if (keyArgs.Key == keyGesture.Key && keyArgs.KeyboardDevice.Modifiers == keyGesture.Modifiers)
            {
                window.Background = Brushes.AntiqueWhite;
            }
        };

    }

After OnSetKeyGestureCallback is called, a user can use a shortcut to perform an action. But OnSetKeyGestureCallback is called only after the menu is opened. So until the user opens the menu, shortcuts are not available. How to make OnSetKeyGestureCallback be called right after the window or the menu is loaded?

A: 

Your DataTemplate for a MenuItem contains a MenuItem. Hence, you will have two MenuItems in your visual tree, one a child of the other. The child will have the properties set correctly but the parent won't.

If you need to specify a property on the MenuItem, you should use ItemContainerStyle:

<MenuItem ItemsSource="{Binding SomeThings}" Header="Menu">
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding MenuTitle}"/>
            <Setter Property="MainWindow.KeyGesture" Value="{Binding KeyGesture}"/>
        </Style>
    </MenuItem>
</MenuItem>

As for why your hookup is not running until the Menu is opened, it's because the child items of the Menu are virtualized and not created until necessary (i.e. until the parent Menu is opened). You can override the Panel used to host the child items too:

<MenuItem>
    <MenuItem.ItemContainerStyle>
        <Style TargetType="MenuItem">
            <Setter Property="Header" Value="{Binding MenuTitle}"/>
            <Setter Property="MainWindow.KeyGesture" Value="{Binding KeyGesture}"/>
        </Style>
    </MenuItem>
    <MenuItem.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel/>
        </ItemsPanelTemplate>
    </MenuItem.ItemsPanel>
</MenuItem>

HTH,
Kent

Kent Boogaart
Thank you for ItemContainerStyle hint! I have even written MenuItem.Style for MenuItem in the DataTemplate to not seem nested inside another one. Now I can rid of it. But the main problem still remains. For example, if I write like this: <ItemsPanelTemplate><StackPanel Loaded="StackPanel_Loaded"/></ItemsPanelTemplate>, the Loaded event is not fired until I open the menu.
Magnetic
A: 

Don't need a solution for this problem. Made a different implementation for shortcuts.

Magnetic