KeyBinding 用作 UserControl,但在 XAML 中使用 属性 元素语法时不起作用
KeyBinding works as UserControl but not when pusing property element syntax in XAML
如标题所述,在使用 属性 元素语法时我无法使 KeyBinding
工作。我所说的工作是指使用 Ctrl+Del
组合键来更改列表框的背景颜色。可以使用组合键或单击按钮,两者都会调用命令,但永远不会调用命令。在调试模式下设置断点将永远不会遇到。
我已经按照文档中的 InputBinding Class example 进行操作,并且只能在使用 UserControl
时使 KeyBinding
工作,并且想了解 为什么 也就是说,什么 我做错了。
下面是代码的 MVCE,使用 属性 元素语法声明,但不起作用。注释掉的是 UserControl
的一行,它封装了 StackPanel
并允许 KeyBinding
工作。取决于在 MainWindow.xaml.cs
.
后面的代码中注释掉每个 PropertyElementSyntax
区域并取消注释每个 UserControlSyntax
区域
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>
Code-behind 对于 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);
}
问题是在没有-UserControl
的情况下,DataContext
是在命令对象初始化之前设置的。
WPF 有一个强大的绑定系统,但它通常依赖于 property-change 通知,通过 INotifyPropertyChanged
。只要您获得正确的操作顺序,某些场景在没有它的情况下也可以工作。但是,如果没有 property-change 通知,如果您错过了向 WPF 提供一些 属性 价值的 window 机会,它不会稍后再试。
当您使用 UserControl
时,UserControl
的绑定初始化发生在您设置 ChangeColorCommand
属性 之后。这只是 WPF 如何初始化 UI 树中的各种对象的产物。但这意味着当 UserControl
的绑定查看 ChangeColorCommand
属性 时,它具有您想要的值。
另一方面,当您将 StackPanel
显式放入 window 的 XAML 时,您为 属性 设置时为时已晚WPF 看看吧。它已经在 InitializeComponent()
调用期间解析了这些绑定。稍后设置 属性 无效。
根据您现在拥有的代码,您可以通过多种方式解决该问题:
- 最简单的方法是在 调用
InitializeCommand()
之后将 DataContext = this;
的赋值移动到 。更新 DataContext
也需要 WPF 更新所有依赖绑定,因此在 InitializeCommand()
调用之后执行此操作可确保 属性 具有您想要的值。
- 在
MainWindow
class 中实现 INotifyPropertyChanged
,并在设置时为 ChangeColorCommand
属性 引发 PropertyChanged
事件。这将让 WPF 知道该值已更改,并且它应该 re-evaluate 任何依赖于它的绑定。
综上所述,我会更进一步:
- 使用
INotifyPropertyChanged
和 ChangeColorCommand
实现正确的视图模型对象,并使用 that 作为数据上下文。让你的 UI 对象做 double-duty 因为 UI 和 属性 绑定源(即视图模型的工作)不适合普通的 WPF 模型,牺牲了好处MVVM 通常会提供,当然也会引入这种奇怪的计时问题,其中 属性 绑定未按预期工作的原因并不明显。
好的,从技术上讲,您可以采用第四种方法,即在 InitializeComponent()
之前调用 InitializeCommand()
。主要问题是,目前,它依赖于直接检索 UI 对象的 属性 的值,而 UI 对象在 InitializeComponent()
之后才会存在被调用。
这让我回到上面的 #3 选项。事实上,您不应该直接访问 UI 对象属性。那应该是你的视图模型中的另一个 属性,你应该更直接地选择初始颜色应该是什么,而不是在启动时从 UI 中获取它。
我承认,这里的设计有一些回旋余地,但您应该尽量让视图模型和 UI 代码彼此分离。
如标题所述,在使用 属性 元素语法时我无法使 KeyBinding
工作。我所说的工作是指使用 Ctrl+Del
组合键来更改列表框的背景颜色。可以使用组合键或单击按钮,两者都会调用命令,但永远不会调用命令。在调试模式下设置断点将永远不会遇到。
我已经按照文档中的 InputBinding Class example 进行操作,并且只能在使用 UserControl
时使 KeyBinding
工作,并且想了解 为什么 也就是说,什么 我做错了。
下面是代码的 MVCE,使用 属性 元素语法声明,但不起作用。注释掉的是 UserControl
的一行,它封装了 StackPanel
并允许 KeyBinding
工作。取决于在 MainWindow.xaml.cs
.
PropertyElementSyntax
区域并取消注释每个 UserControlSyntax
区域
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>
Code-behind 对于 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);
}
问题是在没有-UserControl
的情况下,DataContext
是在命令对象初始化之前设置的。
WPF 有一个强大的绑定系统,但它通常依赖于 property-change 通知,通过 INotifyPropertyChanged
。只要您获得正确的操作顺序,某些场景在没有它的情况下也可以工作。但是,如果没有 property-change 通知,如果您错过了向 WPF 提供一些 属性 价值的 window 机会,它不会稍后再试。
当您使用 UserControl
时,UserControl
的绑定初始化发生在您设置 ChangeColorCommand
属性 之后。这只是 WPF 如何初始化 UI 树中的各种对象的产物。但这意味着当 UserControl
的绑定查看 ChangeColorCommand
属性 时,它具有您想要的值。
另一方面,当您将 StackPanel
显式放入 window 的 XAML 时,您为 属性 设置时为时已晚WPF 看看吧。它已经在 InitializeComponent()
调用期间解析了这些绑定。稍后设置 属性 无效。
根据您现在拥有的代码,您可以通过多种方式解决该问题:
- 最简单的方法是在 调用
InitializeCommand()
之后将DataContext = this;
的赋值移动到 。更新DataContext
也需要 WPF 更新所有依赖绑定,因此在InitializeCommand()
调用之后执行此操作可确保 属性 具有您想要的值。 - 在
MainWindow
class 中实现INotifyPropertyChanged
,并在设置时为ChangeColorCommand
属性 引发PropertyChanged
事件。这将让 WPF 知道该值已更改,并且它应该 re-evaluate 任何依赖于它的绑定。
综上所述,我会更进一步:
- 使用
INotifyPropertyChanged
和ChangeColorCommand
实现正确的视图模型对象,并使用 that 作为数据上下文。让你的 UI 对象做 double-duty 因为 UI 和 属性 绑定源(即视图模型的工作)不适合普通的 WPF 模型,牺牲了好处MVVM 通常会提供,当然也会引入这种奇怪的计时问题,其中 属性 绑定未按预期工作的原因并不明显。
好的,从技术上讲,您可以采用第四种方法,即在 InitializeComponent()
之前调用 InitializeCommand()
。主要问题是,目前,它依赖于直接检索 UI 对象的 属性 的值,而 UI 对象在 InitializeComponent()
之后才会存在被调用。
这让我回到上面的 #3 选项。事实上,您不应该直接访问 UI 对象属性。那应该是你的视图模型中的另一个 属性,你应该更直接地选择初始颜色应该是什么,而不是在启动时从 UI 中获取它。
我承认,这里的设计有一些回旋余地,但您应该尽量让视图模型和 UI 代码彼此分离。