11

I have declared <InputBindings>

<UserControl.InputBindings>
    <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyImageCommand}" />
    <KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteImageCommand}" />
</UserControl.InputBindings>

For testing purposes, I have added buttons bound to those commands too

<Button Command="{Binding CopyImageCommand}" Content="Copy" />
<Button Command="{Binding PasteImageCommand}" Content="Paste" />

I noticed that when the paste button is enabled, when i press Ctrl-V nothing happens. Ctrl-C seems to work. For that, a list box item is selected, I am not sure if it makes any difference. Anyone knows why is my PasteImageCommand not triggering?

I am using .NET 4 btw

UPDATE

A fuller code snipplet

<UserControl x:Class="QuickImageUpload.Views.ShellView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:QuickImageUpload.ViewModels"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.InputBindings>
        <KeyBinding Key="C" Modifiers="Ctrl" Command="{Binding CopyImageCommand}" />
        <KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteImageCommand}" />
    </UserControl.InputBindings>
    <UserControl.DataContext>
        <vm:ShellViewModel />
    </UserControl.DataContext>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="50" />
            <RowDefinition Height="*" />

UPDATE

I found out I need to put the KeyBindings in the MainWindow, but the commands are in the ViewModel, how can i set key bindings in the ShellView which then binds to commands in the ShellViewModel?

Jiew Meng
  • 84,767
  • 185
  • 495
  • 805
  • Can you please post where are InputBinding specified? Its possible you put it in wrong place. – Euphoric Oct 15 '10 at 11:00
  • @Euphoric, I have put my InputBindings in the UserControl ShellView. I discovered it works when I put them in the MainWindow, but i need to set the view model to ShellViewModel, not really correct I think, how can I handle this? – Jiew Meng Oct 19 '10 at 02:44
  • 1
    @JiewMeng: Hi jiew! i have almost same problem. did you found any solution? – Jalal Mar 26 '12 at 21:22
  • @Jalax, I havent been doing C# for some time, I'm afraid I cant remember if I solved this :( – Jiew Meng Mar 28 '12 at 11:52

7 Answers7

6

Make sure that you are not having binding errors. You set the DataContext of the user control, but make sure that the commands can bind to it. Sometimes, WPF just uses the order of appearance, and the DataContext is set later then the commands.

Probably, the output window of VS already shows binding errors for the commands. Try putting the DataContext definition on top (and teach yourself to do this for all views).

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • 1
    moving the input bindings to to bottom of the user control worked for me – joe blogs Dec 28 '18 at 22:30
  • 1
    I had multiple bindings going on and this post helped me realise to just go through each tag and defined the exact datacontext for each component. Thanks! – cwiggo Feb 05 '19 at 14:31
3

To avoid hardcoded KeyBindings, I have derived Josh Smiths RelayCommand-Class and added Shortcut-related stuff:

class UIRelayCommand : RelayCommand, INotifyPropertyChanged
{
    private static Dictionary<ModifierKeys, string> modifierText = new Dictionary<ModifierKeys, string>()
    {
        {ModifierKeys.None,""},
        {ModifierKeys.Control,"Ctrl+"},
        {ModifierKeys.Control|ModifierKeys.Shift,"Ctrl+Shift+"},
        {ModifierKeys.Control|ModifierKeys.Alt,"Ctrl+Alt+"},
        {ModifierKeys.Control|ModifierKeys.Shift|ModifierKeys.Alt,"Ctrl+Shift+Alt+"},
        {ModifierKeys.Windows,"Win+"}
    };

    private Key _key;
    public Key Key
    {
        get { return _key; }
        set { _key = value; RaisePropertyChanged("Key"); RaisePropertyChanged("GestureText"); }
    }

    private ModifierKeys _modifiers;
    public ModifierKeys Modifiers
    {
        get { return _modifiers; }
        set { _modifiers = value; RaisePropertyChanged("Modifiers"); RaisePropertyChanged("GestureText");}
    }

    public string GestureText
    {
        get { return modifierText[_modifiers] + _key.ToString(); }
    }

    public UIRelayCommand(Action<object> execute, Predicate<object> canExecute, Key key, ModifierKeys modifiers)
        : base(execute, canExecute)
    {
        _key = key;
        _modifiers = modifiers;
    }


    public event PropertyChangedEventHandler PropertyChanged;

    public void RaisePropertyChanged(string name)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}

then create the command in the ViewModel:

private ICommand _newFileCommand;
public ICommand NewFileCommand
{
    get
    {
        if (_newFileCommand == null)
            _newFileCommand = new UIRelayCommand(p => OnNewFile(p), p => CanNewFile(p), Key.N, ModifierKeys.Control);
        return _newFileCommand;
    }
}
protected void OnNewFile(object p)
{
    //open file...
}
protected bool CanNewFile(object p)
{
    return true;
}

and bind it in the View:

<Window.InputBindings>
    <KeyBinding Command="{Binding NewFileCommand}" Key="{Binding NewFileCommand.Key}" Modifiers="{Binding NewFileCommand.Modifiers}"  />
</Window.InputBindings>
...
<MenuItem Header="New File" Command="{Binding NewFileCommand}" InputGestureText="{Binding NewFileCommand.GestureText}" />

With this approach I can allow the user to adjust the shortcuts at runtime (in my configuration-window)

0xDEADBEEF
  • 3,401
  • 8
  • 37
  • 66
  • 1
    +1...Excellent solution! One minor typo in the last code snippet: Modifiers="{Binding NewFileCommand.Modifier}" should be Modifiers="{Binding NewFileCommand.Modifiers}". – SWalters Oct 14 '14 at 17:18
1

answer can be found in this link

https://social.msdn.microsoft.com/Forums/vstudio/en-US/395046c8-2cc8-48b4-9642-341ce0c99cc9/key-binding-does-not-work-always-in-mvvm-application?forum=wpf

due to focus issues, inputbindings in the usercontrol should be asigned to the mainwindow using usercontrol code behind

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        this.DataContext = new ViewModel();
        this.Loaded += UserControl1_Loaded;
    }

    void UserControl1_Loaded(object sender, RoutedEventArgs e)
    {
        Window window = Window.GetWindow(this);
        foreach (InputBinding ib in this.InputBindings)
        {
            window.InputBindings.Add(ib);
        }
    }
}
StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Hazem Elamir
  • 51
  • 1
  • 8
0

I had a similar situation where in the Key related events were getting listened to only at the Shell View and was not tunelling to the actual view where the key was pressed. To overcome this problem, I wrote a small attached behavior to set the focus to the user control or the framework element to recieve the focus on initial load and that way the key strokes are listened to by the UI element I want to listen to.

public class FocusBehavior
{
    public static readonly DependencyProperty IsFocusedProperty = 
        DependencyProperty.RegisterAttached("IsFocused", typeof(bool?),typeof(FocusBehavior),
        new UIPropertyMetadata(false, new PropertyChangedCallback(OnFocusChanged)));
    public static bool? GetIsFocused(DependencyObject obj)
    {
        return (bool?)obj.GetValue(IsFocusedProperty);
    }
    public static void SetIsFocused(DependencyObject obj, bool? value)
    {
        obj.SetValue(IsFocusedProperty, value);
    }
    private static void OnFocusChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var frameworkElement = sender as FrameworkElement;
        if (frameworkElement == null) return;
        if (args.OldValue == null) return;
        if (args.NewValue == null) return;
        if ((bool)args.NewValue)
        {
            frameworkElement.Loaded += OnFrameworkElementLoaded;
        }
    }

    private static void OnFrameworkElementLoaded(object sender, RoutedEventArgs args)
    {
        var frameworkElement = sender as FrameworkElement;
        frameworkElement.Focus();
        frameworkElement.Loaded -= OnFrameworkElementLoaded;
        var textControl = frameworkElement as JHATextEditor;
        if (textControl == null) return;
        textControl.SelectAll();
    }
}

And used it like this in one of my list views as below -

<GridViewColumn Width="Auto" Header="Value">
                    <GridViewColumn.CellTemplate>
                        <DataTemplate>
                            <Grid HorizontalAlignment="Stretch" MinWidth="100">
                                <TextBlock Text="{Binding FieldValue}" />
                            </Grid>
                            <DataTemplate.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                    <Setter Property="local:FocusBehavior.IsFocused" TargetName="FieldValueEditor" Value="True" />
                                </DataTrigger>
                            </DataTemplate.Triggers>
                        </DataTemplate>
                    </GridViewColumn.CellTemplate>
                </GridViewColumn>

Hope this helps.

-VJ

VBK
  • 23
  • 7
0

In this case you can provide keybindings in your RoutedCommand declaration:

public static RoutedCommand PasteImageCommand = new RoutedCommand("PasteImageCommand", typeof(YourType), new InputGestureCollection { new KeyGesture(Key.V, ModifierKeys.Control)});

This should work.

Alexzander
  • 345
  • 5
  • 8
0

Try this:

<UserControl.InputBindings>
        <KeyBinding Key="C" Modifiers="Control" Command="{Binding CopyImageCommand}" />
        <KeyBinding Key="V" Modifiers="Control" Command="{Binding PasteImageCommand}" />
    </UserControl.InputBindings>
user2728832
  • 23
  • 1
  • 4
0

Are you using 3.5 or 4?

In 3.5 its "feature". UserControl.InputBindings is not part of dataContext tree so you cannot bind to items of class, that is bound to parent. eg. DataBinding wont work and you need to set DataBinding or whole KeyBinding in code by hand.

Its fixed in 4.

Euphoric
  • 12,645
  • 1
  • 30
  • 44