【问题标题】:WPF UserControl, with Buttons and KeyBindingsWPF 用户控件,带有按钮和键绑定
【发布时间】:2016-01-15 00:59:56
【问题描述】:

我正在尝试在 WPF 中实现拨号程序。我有一个窗口,里面有一个用户控件。用户控件有很多按钮,但用户也可以使用数字键盘输入数字。

我创建了一个小示例项目来显示我的位置:

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); }
        }
    }
}

目标:

  • 一旦加载窗口,当用户按下A键时,CommandDialValue就会被执行;
  • 当用户按下 Enter 时,会显示一个带有文本“KeyBinding”的消息框。 CommandAcceptValue 必须从 KeyBinding 调用,而不是从按钮调用;

问题:

  • 加载窗口时,KeyBindings 不会执行。当我单击 UserControl 中某处的按钮时,它们会被执行;

  • 当我按下回车时,按钮的命令被执行,但我希望用户控件的 KeyBinding 被执行;

  • 此拨号程序必须保存在 UserControl(或 ControlTemplate 或 DataTemplate)中,因为它包含在一个非常精细的窗口中。

  • 我不想将 KeyBindings 放在 Window 上,因为那样的话 UserControl 是不可重用的,并且因为它的 DataContext 与用户控件不同。

更新:

我通过在所有按钮上设置Focusable="False" 解决了第二个问题。

【问题讨论】:

    标签: c# wpf xaml user-controls key-bindings


    【解决方案1】:

    为了防止按钮获得焦点,我为所有按钮设置了Focusable="False"

    为了在窗口打开时设置焦点,我在 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}}

    【讨论】:

      【解决方案2】:

      这是一个专注的问题。当您的窗口第一次加载时,您的用户控件没有焦点。所以键绑定的手势不会拦截你的按键。在第一次加载应用程序时,您必须将 Focus 交给您的用户控制。 (Focusable =“True”(我不知道这是否有帮助,但我相信FocusManager 会有所帮助))。那么你的关键手势就会很好地发挥作用。

      【讨论】:

      • 你能更精确一点吗?我在 UserControl 上尝试了FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Mode=Self}}",但没有帮助。
      • 给你一个 X:Name property=CustomName,然后 FocusManager.FocusedElement="{Binding ElementName=CustomName}
      猜你喜欢
      • 2011-08-20
      • 1970-01-01
      • 2011-01-16
      • 2013-07-01
      • 1970-01-01
      • 2016-08-29
      • 1970-01-01
      • 2013-06-06
      相关资源
      最近更新 更多