Question

I need to bind the visibility of a control on a WPF UserControl to the state of the Alt Key, if somehow possible via a converter. The Button should only be visible if the ALT Key is being pressed down, the solution should not be integrated in the Code Behind file, since I'm working in a strict MVVM pattern using PRISM/Unity.

A perfect solution would include writing a new converter that would be able to convert the state of a keyboard key to the Visiblity property of a user control, but I have little experience in converters and wasn't able to come up with a solution by myself.

Was it helpful?

Solution

Here is a complete example

Xaml

<Window x:Class="Playground.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Playground"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        Title="MainWindow" Height="350" Width="525">
    <Window.InputBindings>
        <KeyBinding Modifiers="Alt" Key="LeftAlt" Command="{Binding AltPressedCommand}" />
    </Window.InputBindings>
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="boolToVisibilityConverter"/>
    </Window.Resources>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="PreviewKeyUp">
            <i:InvokeCommandAction Command="{Binding AltUnpressedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Grid>
        <Button Content="My Button" Visibility="{Binding IsAltPressed, Converter={StaticResource boolToVisibilityConverter}}"/>
    </Grid>
</Window>

ViewModel

public class MainWindowViewModel : NotificationObject
{
    public MainWindowViewModel()
    {
        AltPressedCommand = new DelegateCommand(() => IsAltPressed = true);
        AltUnpressedCommand = new DelegateCommand(() => IsAltPressed = false);
    }

    public DelegateCommand AltPressedCommand { get; set; }

    public DelegateCommand AltUnpressedCommand { get; set; }

    private bool _IsAltPressed;
    public bool IsAltPressed
    {
        get { return _IsAltPressed; }
        set
        {
            if (value != _IsAltPressed)
            {
                _IsAltPressed = value;
                RaisePropertyChanged("IsAltPressed");
            }
        }
    }
}

Explanation

The visibility of the control is binded to a boolean property via BooleanToVisibilityConverter. Then I use two commands. One fired when the Alt key is being pressed using KeyBinding, and the second is fired when the key up occurs. I left out a check of the Alt key when the key up occurs that you should add. If you want to purely use MVVM this could get tricky because you need to send a parameter to the command stating the key being pressed up.

Edit

I use the following behavior to pass the key parameter from the PreviewKeyUp event

public class PreviewKeyUpBehavior : Behavior<UIElement>
{
    #region Properties

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }

    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.Register("Command", typeof(ICommand), typeof(PeviewKeyUpBehavior));

    #endregion

    #region Methods

    protected override void OnAttached()
    {
        AssociatedObject.PreviewKeyUp += OnPreviewKeyUp;
        base.OnAttached();
    }

    private void OnPreviewKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
    {
        if (Command == null) return;


        // Execute command and send the key as the command parameter
        Command.Execute(e.Key == Key.System ? e.SystemKey : e.Key);
    } 

    #endregion
}

This will raise the binded command when the PreviewKeyUp is fired and send the key as the commands parameter. I then altered the code in the View and ViewModel as follows:

<!-- Used behaviors instead of triggers -->
<i:Interaction.Behaviors>
    <local:PreviewKeyUpBehavior Command="{Binding KeyUnpressedCommand}"/>
</i:Interaction.Behaviors>

Changed the command to take a nullable key parameter

public DelegateCommand<Key?> KeyUnpressedCommand { get; set; }

And implemented it

KeyUnpressedCommand = new DelegateCommand<Key?>(key => 
{
    if (key == Key.LeftAlt)
        IsAltPressed = false;
});

Hope this helps

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top