KeyBinding充当UserControl,但在XAML

时间:2019-07-18 22:49:33

标签: c# wpf key-bindings

正如标题所述,使用属性元素语法时,我无法KeyBinding工作。通过工作,我的意思是使用Ctrl+Del组合键更改列表框的背景颜色。可以使用组合键或单击按钮,两者都会调用该命令,但是永远不会调用该命令。在调试模式下设置断点时,将永远不会遇到它。

我遵循文档中的InputBinding Class example,并且在使用KeyBinding时只能让UserControl正常工作,并且想了解为什么 ,以及什么我做错了。

下面是使用属性元素语法声明的代码不起作用时的MVCE。被注释掉的是UserControl的一行,其中封装了StackPanel并允许KeyBinding工作。视情况而定,在PropertyElementSyntax后面的代码中注释掉每个UserControlSyntax区域,并取消注释每个MainWindow.xaml.cs区域。

MainWindow.xaml:

<Window x:Class="LearningKeyBindingWPFApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:LearningKeyBindingWPFApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="300">
    <!--<local:UserControl1 x:Name="CustomColorPicker" />-->
    <StackPanel Margin="0,40,0,0">
        <StackPanel.InputBindings>
            <KeyBinding Command="{Binding ChangeColorCommand}"
                        CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}"
                        Key="{Binding ChangeColorCommand.Key}"
                        Modifiers="{Binding ChangeColorCommand.ModifierKeys}" />
            <MouseBinding Command="{Binding ChangeColorCommand}"
                          CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}"
                          MouseAction="{Binding ChangeColorCommand.MouseAction}" />
        </StackPanel.InputBindings>
        <Button Content="Change Color" 
                Command="{Binding ChangeColorCommand}"
                CommandParameter="{Binding ElementName=ColorPicker, Path=SelectedItem}" />
        <ListBox Name="ColorPicker"
                 xmlns:sys="clr-namespace:System;assembly=mscorlib"
                 SelectedIndex="0">
            <sys:String>Red</sys:String>
            <sys:String>Green</sys:String>
            <sys:String>Blue</sys:String>
            <sys:String>Yellow</sys:String>
            <sys:String>Orange</sys:String>
            <sys:String>Purple</sys:String>
        </ListBox>
    </StackPanel>
</Window>

MainWindow.xaml.cs的代码隐藏:

public MainWindow()
{
    DataContext = this;

    InitializeComponent();
    InitializeCommand();

    #region UserControlSyntax
    //CustomColorPicker.ColorPicker.Focus();
    #endregion

    #region PropertyElementSyntax
    ColorPicker.Focus();
    #endregion
}

public SimpleDelegateCommand ChangeColorCommand { get; private set; }

private SolidColorBrush _originalColor;

private void InitializeCommand()
{
    #region UserControlSyntax
    //_originalColor = (SolidColorBrush)CustomColorPicker.ColorPicker.Background;
    #endregion

    #region PropertyElementSyntax
    _originalColor = (SolidColorBrush)ColorPicker.Background;
    #endregion

    ChangeColorCommand = new SimpleDelegateCommand(ChangeColor)
    {
        Key = Key.Delete,
        ModifierKeys = ModifierKeys.Control
    };
}

private void ChangeColor(object colorString)
{
    if (colorString == null)
    {
        return;
    }

    var selectedColor = SelectedColor((string)colorString);

    #region UserControlSyntax
    //if (CustomColorPicker.ColorPicker.Background == null)
    //{
    //    CustomColorPicker.ColorPicker.Background = selectedColor;
    //    return;
    //}

    //CustomColorPicker.ColorPicker.Background = ((SolidColorBrush)CustomColorPicker.ColorPicker.Background).Color == selectedColor.Color
    //        ? _originalColor
    //        : selectedColor;
    #endregion

    #region PropertyElementSyntax
    if (ColorPicker.Background == null)
    {
        ColorPicker.Background = selectedColor;
        return;
    }

    var isColorIdentical = ((SolidColorBrush)ColorPicker.Background).Color == selectedColor.Color;
    ColorPicker.Background = isColorIdentical
            ? _originalColor
            : selectedColor;
    #endregion
}

private SolidColorBrush SelectedColor(string value)
{
    #region UserControlSyntax
    //var selectedColor = (Color)ColorConverter.ConvertFromString(value);
    #endregion

    #region PropertyElementSyntax
    var selectedColor = (Color)ColorConverter.ConvertFromString((string)ColorPicker.SelectedItem);
    #endregion

    return new SolidColorBrush(selectedColor);
}

1 个答案:

答案 0 :(得分:1)

问题在于,在没有UserControl的情况下,DataContext是在初始化命令对象之前设置的。

WPF具有强大的绑定系统,但是它通常依赖于通过INotifyPropertyChanged进行的属性更改通知。只要您正确确定操作顺序,某些情况下就可以解决此问题。但是,如果没有财产变更通知,如果您错过了向WPF展示一些财产价值的机会之窗,那么以后将不再尝试。

使用UserControl时,在设置UserControl属性之后,将初始化ChangeColorCommand的绑定。这只是WPF如何初始化UI树中的各种对象的产物。但这意味着,当UserControl的绑定查看ChangeColorCommand属性时,它便具有所需的值。

另一方面,当您将StackPanel明确地放入窗口的XAML中时,为WPF设置属性以使其可见时为时已晚。它已经在InitializeComponent()调用期间解决了这些绑定。稍后设置该属性无效。

鉴于已有的代码,您可以通过以下几种方式解决:

  1. 最简单的方法是在对DataContext = this;的调用之后,将InitializeCommand()的分配移至 。更新DataContext要求WPF也更新所有相关的绑定,因此在InitializeCommand()调用之后执行此操作可确保属性具有所需的值。
  2. INotifyPropertyChanged类中实施MainWindow,并在设置PropertyChanged属性时引发ChangeColorCommand事件。这将使WPF知道该值已更改,并且它应该重新评估依赖于该值的任何绑定。

所有这些,我再说一遍:

  1. 使用INotifyPropertyChangedChangeColorCommand来实现适当的视图模型对象,并使用 that 作为数据上下文。由于UI和属性绑定源(即视图模型的工作)都不适合常规WPF模型,因此使UI对象承担双重职责,牺牲了MVVM通常提供的好处,并且当然引入了这种奇怪的计时尚不清楚为什么属性绑定无法按预期工作的情况。


好的,从技术上讲,您可以采用第四种方法,即将呼叫InitializeCommand()放在InitializeComponent()之前。目前的主要问题是,它依赖于直接获取UI对象属性的值,并且该UI对象在调用InitializeComponent()之后才存在。

这使我回到上面的#3选项。事实是,您不应该直接访问UI对象属性。那应该是视图模型中的另一个属性,并且应该对初始颜色应该做一个更直接的选择,而不只是在启动时从UI中获取它。

我承认,这里有一些设计空间,但是您应该尽量保持视图模型和UI代码彼此分离。