WPF UserControl,带有按钮和键绑定
WPF UserControl, with Buttons and KeyBindings
我正在尝试在 WPF 中实现拨号器。我有一个 window,里面有一个用户控件。用户控件有很多按钮,但用户也可以使用数字键盘输入数字。
我创建了一个小示例项目来展示我现在的位置:
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); }
}
}
}
目标:
- 一旦window被加载,当用户按下A键时,CommandDialValue被执行;
- 当用户按 Enter 时,会显示一个消息框,其中包含文本 "KeyBinding"。 CommandAcceptValue 必须从 KeyBinding 调用,而不是从按钮调用;
问题:
加载 window 时,KeyBindings 不会执行。当我单击 UserControl 中某处的按钮时执行它们;
当我回车的时候,按钮的命令被执行了,但是我想要用户控件的KeyBinding被执行;
此拨号程序必须保存在 UserControl(或 ControlTemplate 或 DataTemplate)中,因为它包含在非常精细的 window 中。
我不想将 KeyBindings 放在 Window 上,因为这样 UserControl 不可重用,而且它的 DataContext 与用户控件不同。
更新:
我通过在所有按钮上设置 Focusable="False"
解决了第二个问题。
是Focus的问题。当您的 window 第一次加载时,您的用户控件没有焦点。所以按键绑定的手势不会拦截你的按键。在应用程序第一次加载时,您必须将焦点交给您的用户控制。 (Focusable ="True"(我不知道这是否有帮助,但我相信 FocusManager 会有所帮助))。那么你的关键手势就可以正常工作了。
为了防止按钮获得焦点,我为所有按钮设置了Focusable="False"
。
为了在 window 打开时设置焦点,我在 UserControl 上设置了 Focusable="True"
,在 Loaded 事件上我调用了 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();
}
我发现 FocusManager.FocusedElement
的组合没有任何效果。我尝试了 {Binding ElementName=myUserControl}
和 {Binding RelativeSource={RelativeSource Self}}
.
我正在尝试在 WPF 中实现拨号器。我有一个 window,里面有一个用户控件。用户控件有很多按钮,但用户也可以使用数字键盘输入数字。
我创建了一个小示例项目来展示我现在的位置:
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); }
}
}
}
目标:
- 一旦window被加载,当用户按下A键时,CommandDialValue被执行;
- 当用户按 Enter 时,会显示一个消息框,其中包含文本 "KeyBinding"。 CommandAcceptValue 必须从 KeyBinding 调用,而不是从按钮调用;
问题:
加载 window 时,KeyBindings 不会执行。当我单击 UserControl 中某处的按钮时执行它们;
当我回车的时候,按钮的命令被执行了,但是我想要用户控件的KeyBinding被执行;
此拨号程序必须保存在 UserControl(或 ControlTemplate 或 DataTemplate)中,因为它包含在非常精细的 window 中。
我不想将 KeyBindings 放在 Window 上,因为这样 UserControl 不可重用,而且它的 DataContext 与用户控件不同。
更新:
我通过在所有按钮上设置 Focusable="False"
解决了第二个问题。
是Focus的问题。当您的 window 第一次加载时,您的用户控件没有焦点。所以按键绑定的手势不会拦截你的按键。在应用程序第一次加载时,您必须将焦点交给您的用户控制。 (Focusable ="True"(我不知道这是否有帮助,但我相信 FocusManager 会有所帮助))。那么你的关键手势就可以正常工作了。
为了防止按钮获得焦点,我为所有按钮设置了Focusable="False"
。
为了在 window 打开时设置焦点,我在 UserControl 上设置了 Focusable="True"
,在 Loaded 事件上我调用了 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();
}
我发现 FocusManager.FocusedElement
的组合没有任何效果。我尝试了 {Binding ElementName=myUserControl}
和 {Binding RelativeSource={RelativeSource Self}}
.