lte__ lte__ - 15 days ago 6
C# Question

C# with WPF - Keyboard not binding?

I want to enable, disable and re-enable certain keyboard strokes in my WPF program based on certain conditions. I have the binding currently like this:

//MainWindow.xaml
...
<Window.InputBindings>
<KeyBinding Command="{Binding UpCommand}" Key="Up"/>
<KeyBinding Command="{Binding DownCommand}" Key="Down"/>
<KeyBinding Command="{Binding LeftCommand}" Key="Left"/>
<KeyBinding Command="{Binding RightCommand}" Key="Right"/>
</Window.InputBindings>
...


And then in the ViewModel:

//MLViewModel.cs
class MLViewModel : ViewModelBase
{
public DelegateCommand UpCommand { get; private set; }
public DelegateCommand DownCommand { get; private set; }
public DelegateCommand LeftCommand { get; private set; }
public DelegateCommand RightCommand { get; private set; }

public MLViewModel(MLModel model)
{
...
Lvl1Command = new DelegateCommand(param => { SetUpGame(MLModel.Level.Easy); });
Lvl2Command = new DelegateCommand(param => { SetUpGame(MLModel.Level.Medium); });
Lvl3Command = new DelegateCommand(param => { SetUpGame(MLModel.Level.Hard); });

UpCommand = new DelegateCommand(param => { _model.MoveUp(); RefreshTable(); });
DownCommand = new DelegateCommand(param => { _model.MoveDown(); RefreshTable();});
LeftCommand = new DelegateCommand(param => { _model.MoveLeft(); RefreshTable(); });
RightCommand = new DelegateCommand(param => { _model.MoveRight(); RefreshTable(); });
}

private void SetUpGame(MLModel.Level level)
{
// setting up the field etc.
}

private void Model_GameOver(object sender, MLEventArgs e)
{
// handling the event of the game being over
}
}


Now, this works as supposed, except that the key binding is enabled all the time while the program is running. The gamefield doesn't disappear after a game is finished, but I don't want the user to be able to move (~use the keys) until she/he starts a new game via
SetUpGame()
. And that's why I'd like to move the
[Up-Down-Left-Right]Command
binding to
SetUpGame()
instead of the constructor (and unbind it in
Model_Gameover
). However, if I do move the instantiation of the new DelegateCommands to
SetUpGame()
, the key binding doesn't happen and I'm unable to play.
Lvl[1-2-3]Command
works as expected, but the keyboard binding doesn't (it does work, however, when it's in the constructor).

How can I make sure the key binding is enabled after
SetUpGame
until a game is finished, but is disabled after
Model_GameOver
until
SetUpGame
is run again? Why is placing the
new DelegateCommand
s to
SetUpGame()
not working? Thank you!

ADDITION

I have IPropertyChanged implemented as follows:

namespace SavageMaci.ViewModel
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
protected ViewModelBase() { }

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}


However, even if I'm trying

DownCommand = new DelegateCommand(param => { _model.MoveDown(); RefreshTable(); OnPropertyChanged(); });


it doesn't work. What am I missing?

Answer

Your commands are still bound properties, just with a special type ICommand. That means that it works the same as any other binding. If you want elements to pick up a change to a bound property they need to raise PropertyChanged. Because your commands are using auto-properties, that event isn't raised.

The reason you don't normally have to do this is property assignments in a constructor happen before the bindings evaluate.

As for "removing" the bindings; you could set them to null (and raise PropertyChanged as appropriate) but a better solution would be to use a DelegateCommand (or a custom command object) that supports CanExecute/CanExecuteChanged and use that to "disable" the commands when the program isn't in a valid state.