Reputation: 1342
I'm trying to implement a dialer in WPF. I have a window, and inside it a user control. The user control has lots of buttons, but the user can also use the num pad to enter numbers.
I created a small sample project to show where I'm at:
MainWindow.xaml
<Window x:Class="wpf_dialer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:wpf_dialer"
Title="MainWindow" Height="200" Width="525">
<Window.Resources>
<local:DialerViewModel x:Key="myViewModel" />
</Window.Resources>
<Grid DataContext="{StaticResource myViewModel}">
<local:Dialer />
</Grid>
</Window>
Dialer.xaml
<UserControl x:Class="wpf_dialer.Dialer"
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:local="clr-namespace:wpf_dialer"
mc:Ignorable="d"
d:DesignHeight="200" d:DesignWidth="300"
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded">
<UserControl.InputBindings>
<KeyBinding Key="A" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="." />
<KeyBinding Key="Back" Command="{Binding CommandDialValue, Mode=OneTime}" CommandParameter="Back" />
<KeyBinding Key="Enter" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="KeyBinding" />
</UserControl.InputBindings>
<UniformGrid Columns="1">
<TextBox IsEnabled="False" Text="{Binding DialedValue, Mode=OneWay}" MinWidth="200" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10"/>
<Button Content="OK" Margin="60,30" Command="{Binding CommandAcceptValue, Mode=OneTime}" CommandParameter="Button" />
</UniformGrid>
</UserControl>
DialerViewModel.cs
using System;
using System.ComponentModel;
using System.Windows.Input;
namespace wpf_dialer
{
class DialerViewModel : INotifyPropertyChanged
{
private Random RAND = new Random();
private string _dialed_value = "00";
public string DialedValue
{
get { return _dialed_value; }
set
{
if (_dialed_value == value) return;
_dialed_value = value;
RaisePropertyChanged("DialedValue");
}
}
public ICommand CommandDialValue { get { return new CommandImpl(DialValue); } }
public ICommand CommandAcceptValue { get { return new CommandImpl(Alert); } }
private void DialValue(object parameter)
{
if (parameter.ToString() == "Back")
{
if (DialedValue.Length > 0)
{
DialedValue = DialedValue.Substring(0, DialedValue.Length - 1);
}
}
else
{
DialedValue += RAND.Next(0, 10).ToString();
}
}
private void Alert(object parameter)
{
System.Windows.MessageBox.Show(parameter.ToString());
}
#region INotifyPropertyChanged
protected void RaisePropertyChanged(string property_name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private class CommandImpl : ICommand
{
private readonly Action<object> _action = null;
public CommandImpl(Action<object> action)
{
_action = action;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) { return true; }
public void Execute(object parameter) { _action(parameter); }
}
}
}
Objectives:
Problems:
When the window is loaded, the KeyBindings don't execute. They are executed when I click a button somewhere in the UserControl;
When I press Enter, the button's command is executed, but I want the user control's KeyBinding to be executed;
This dialer must be held in a UserControl (or a ControlTemplate, or DataTemplate), because it's contained in a very elaborate window.
I don't want to put the KeyBindings on the Window, because then the UserControl is not reusable, and because its DataContext is not the same as the user control.
UPDATE:
I solved the second problem by setting Focusable="False"
on all buttons.
Upvotes: 2
Views: 7752
Reputation: 1342
To prevent the buttons from gaining focus, I set Focusable="False"
for all buttons.
To set the focus when the window opens, I set Focusable="True"
on the UserControl, and on the Loaded event I called Focus()
.
Dialer.xaml
d:DataContext="{d:DesignInstance local:DialerViewModel}"
Loaded="UserControl_Loaded" Focusable="True">
<UserControl.InputBindings>
Dialer.xaml.cs
private void UserControl_Loaded(object sender, RoutedEventArgs e) {
Focus();
}
I found no combination of FocusManager.FocusedElement
that worked. I tried {Binding ElementName=myUserControl}
, and {Binding RelativeSource={RelativeSource Self}}
.
Upvotes: 4
Reputation: 1235
It's a question of Focus. When your window is loaded for the first time, your user control does not have Focus. So key bindings' gesture will not intercept your keypress. You have, at the first app loading time, to give Focus to your user control. (Focusable ="True"(i don't know if this helps but i am sure the FocusManager will helps)). Then your key gestures will work well.
Upvotes: 2