0

None of my menu shortcuts are working.

I have read a bit up on this issue and according to the answer here it implies that I need to use commands instead of clicks.

Here is the menu structure:

<MenuItem x:Uid="MenuItem_10" Header="Zoom In" InputGestureText="CTRL +" Click="menuViewZoomInOutScaleFactor" Tag="25">
    <MenuItem.InputBindings>
        <KeyBinding x:Uid="KeyBinding_7" Key="OemPlus" Modifiers="Ctrl" />
    </MenuItem.InputBindings>
</MenuItem>
<MenuItem x:Uid="MenuItem_11" Header="Zoom Out" InputGestureText="CTRL -" Click="menuViewZoomInOutScaleFactor" Tag="-25">
    <MenuItem.InputBindings>
        <KeyBinding x:Uid="KeyBinding_8" Key="OemMinus" Modifiers="Ctrl"/>
    </MenuItem.InputBindings>
</MenuItem>
<Separator x:Uid="Separator_7"/>
<MenuItem x:Uid="MenuItem_12" Header="400%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=400}, Mode=OneWay}" Tag="400" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_13" Header="300%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=300}, Mode=OneWay}" Tag="300" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_14" Header="250%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=250}, Mode=OneWay}" Tag="250" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_15" Header="200%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=200}, Mode=OneWay}" Tag="200" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_16" Header="175%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=175}, Mode=OneWay}" Tag="175" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_17" Header="150%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=150}, Mode=OneWay}" Tag="150" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_18" Header="125%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=125}, Mode=OneWay}" Tag="125" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_19" Header="100%" IsCheckable="True" InputGestureText="CTRL + 0"  IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=100}, Mode=OneWay}" Tag="100" Click="menuViewZoomScaleFactor">
    <MenuItem.InputBindings>
        <KeyBinding x:Uid="KeyBinding_9" Key="D0"  Modifiers="Ctrl"/>
    </MenuItem.InputBindings>
</MenuItem>
<MenuItem x:Uid="MenuItem_20" Header="75%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=75}, Mode=OneWay}" Tag="75" Click="menuViewZoomScaleFactor"/>
<MenuItem x:Uid="MenuItem_21" Header="50%" IsCheckable="True" IsChecked="{Binding Source={x:Static Properties:Settings.Default}, Path=ZoomFactor, Converter={ValueConverters:IsIntegerEqual To=50}, Mode=OneWay}" Tag="50" Click="menuViewZoomScaleFactor"/>
<Separator x:Uid="Separator_8"/>
<MenuItem x:Uid="MenuItem_22" Header="Custom..."/>

As you can see, three of the menu items shoudl be associated with:

  1. Ctrl + 0
  2. Ctrl +
  3. Ctrl -

I couldn't find those specific letters in the Keys list and settled for D0, OemPlus and OemMinus. Eitherway, none of my key bindings are actually getting processed. Why?

I can't see how I can convert from using clicks to commands. Up until now, all of my commands have been tied in with the view model. But in this instance I need the command to be associated with the code behind.

Here are my current click handlers:

private void menuViewZoomScaleFactor(object sender, RoutedEventArgs e)
{
    SetZoomFactor(Int32.Parse((String)((MenuItem)sender).Tag));
}

private void menuViewZoomInOutScaleFactor(object sender, RoutedEventArgs e)
{
    int iStep = Int32.Parse((String)((MenuItem)sender).Tag);

    if ((iStep == 25 && Settings.Default.ZoomFactor <= 375) ||
        (iStep == -25 && Settings.Default.ZoomFactor >= 75))
    {
        SetZoomFactor(Settings.Default.ZoomFactor + iStep);
    }
}

private void SetZoomFactor(int iZoomFactor)
{
    Settings.Default.ZoomFactor = iZoomFactor;

    IServiceProvider serviceProvider = myWorkbookView.Document as IServiceProvider;

    Guid SID_SWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
    Guid serviceGuid = SID_SWebBrowserApp;

    Guid iid = typeof(SHDocVw.IWebBrowser2).GUID;

    SHDocVw.IWebBrowser2 WebBrowser = (SHDocVw.IWebBrowser2)serviceProvider.QueryService(ref serviceGuid, ref iid);
    WebBrowser.ExecWB(SHDocVw.OLECMDID.OLECMDID_OPTICAL_ZOOM, SHDocVw.OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, Settings.Default.ZoomFactor);
}

So I have to get my menu key bindings functional.

Community
  • 1
  • 1
Andrew Truckle
  • 17,769
  • 16
  • 66
  • 164
  • @EliArbel I haave just looked that over. I don't understand how a static class that is outside the MainWindow code behind class is goign to have access to the mainwindow content. – Andrew Truckle Jul 15 '16 at 09:26
  • @EliArbel I am really confused. I can't see how I can use a command define din the code behind. – Andrew Truckle Jul 15 '16 at 09:38
  • @EliArbel I have used commands. There are buttons on my window that invoke commands, and use kindbindings to also invoke the commands. But those commands are defined in the viewmodel. These menu items only affect the actual main window itself. It has nothing to do with the view model. You can see my current click handlers. Are you able to provide a proposal for replicating that via a command handler? Perhaps I am missing something. – Andrew Truckle Jul 15 '16 at 09:48

1 Answers1

2

If you want to use InputBindings in WPF you have to use commands. You'll need to assign commands to menu items as well as add command bindings and input gestures. You can use CommandParameters to pass arguments to the command binding handlers.

Think of the routed command object as a key that defines the link between the command source (the menu item or an input binding) and the command binding. The command bindings define the scopes of the visual tree where the command can run.

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow">
    <Window.CommandBindings>
        <CommandBinding Command="IncreaseZoom"
                        Executed="IncreaseZoom_OnExecuted" />
        <CommandBinding Command="Zoom"
                        Executed="Zoom_OnExecuted" />
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Gesture="Ctrl+Plus"
                    Command="IncreaseZoom" />
    </Window.InputBindings>
    <Grid>
        <Menu>
            <MenuItem Header="View">
                <MenuItem x:Uid="MenuItem_10"
                          Header="Zoom In"
                          InputGestureText="CTRL +"
                          Command="IncreaseZoom" />
                <MenuItem x:Uid="MenuItem_12"
                          Header="400%"
                          IsCheckable="True"
                          IsChecked="{Binding Whatever}"
                          Command="Zoom">
                    <MenuItem.CommandParameter>
                        <system:Int32>400</system:Int32>
                    </MenuItem.CommandParameter>
                </MenuItem>
            </MenuItem>
        </Menu>
    </Grid>
</Window>

Code behind:

private void IncreaseZoom_OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
    IncrementZoom(25);
}

private void Zoom_OnExecuted(object sender, ExecutedRoutedEventArgs e)
{
    var zoom = (int)e.Parameter;
    SetZoom(zoom);
}

If you don't find a suitable command in the built-in commands, you can define your own in a static class:

public static class MyCommands
{
    public static readonly RoutedCommand SampleCommand = new RoutedCommand(
        nameof(SampleCommand), typeof(MyCommands), 
        new InputGestureCollection { new KeyGesture(Key.B, ModifierKeys.Alt) });
}

As you can see, you can even define a default input gesture so you won't need to specify an additional input binding in XAML (just the command binding). To use this class in XAML:

<MenuItem Command="{x:Static m:MyCommands.SampleCommand}" />
Eli Arbel
  • 22,391
  • 3
  • 45
  • 71
  • OK, I made progress, I can get it to fire the command. But unless I click the menu item, the parameter is null. If I just use kkeypress the parameter is null. If I click menu item, the parameter is valid. – Andrew Truckle Jul 15 '16 at 10:29
  • OK, that works. Getting there. But ... if I may ask, is it not possible to just use the "Tag" value as the pamater to the command and key binding? That way I only have one command. Otherwise I must define two commands. I will if required. – Andrew Truckle Jul 15 '16 at 10:32
  • One question: where is the built in list of commands defined? – Andrew Truckle Jul 15 '16 at 17:22
  • It must have been a fluke that the ones I used were ok. For example I called on refresh. My handler works that I wrote. I tried to find the names we used for zoom but can see them. My logic handlers work which is the main thing. Just wondered if there was one full list of valid strings. – Andrew Truckle Jul 15 '16 at 20:04