Lee*_*eil 13 data-binding wpf mvvm
我刚刚开始使用MVVM并且我在查明如何将文本框中的按键绑定到视图模型中的ICommand时遇到问题.我知道我可以在代码隐藏中做到这一点,但我试图尽可能地避免这种情况.
更新:到目前为止,如果你有混合sdk或你没有遇到与我正在进行的交互dll的问题,那么这些解决方案都很好.除了必须使用混合sdk之外,还有其他更通用的解决方案吗?
首先,如果要绑定RoutedUICommand,则很容易 - 只需添加到UIElement.InputBindings集合:
<TextBox ...>
<TextBox.InputBindings>
<KeyBinding
Key="Q"
Modifiers="Control"
Command="my:ModelAirplaneViewModel.AddGlueCommand" />
Run Code Online (Sandbox Code Playgroud)
当您尝试设置Command ="{Binding AddGlueCommand}"以从ViewModel获取ICommand时,您的麻烦就开始了.由于Command不是DependencyProperty,因此无法在其上设置Binding.
您的下一次尝试可能是创建一个附加属性BindableCommand,它具有更新Command的PropertyChangedCallback.这允许您访问绑定,但是由于InputBindings集合未设置InheritanceContext,因此无法使用FindAncestor查找ViewModel.
显然,您可以创建一个可以应用于TextBox的附加属性,该属性将遍历调用BindingOperations.GetBinding的所有InputBindings,以查找Command绑定并使用显式源更新这些Bindings,从而允许您执行以下操作:
<TextBox my:BindingHelper.SetDataContextOnInputBindings="true">
<TextBox.InputBindings>
<KeyBinding
Key="Q"
Modifiers="Control"
my:BindingHelper.BindableCommand="{Binding ModelGlueCommand}" />
Run Code Online (Sandbox Code Playgroud)
这个附加属性很容易实现:在PropertyChangedCallback上,它将在DispatcherPriority.Input上安排"刷新"并设置一个事件,以便在每次DataContext更改时重新安排"刷新".然后在"刷新"代码中,只需在每个InputBinding上设置DataContext:
...
public static readonly SetDataContextOnInputBindingsProperty = DependencyProperty.Register(... , new UIPropetyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
var element = obj as FrameworkElement;
ScheduleUpdate(element);
element.DataContextChanged += (obj2, e2) =>
{
ScheduleUpdate(element);
};
}
});
private void ScheduleUpdate(FrameworkElement element)
{
Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() =>
{
UpdateDataContexts(element);
})
}
private void UpdateDataContexts(FrameworkElement target)
{
var context = target.DataContext;
foreach(var inputBinding in target.InputBindings)
inputBinding.SetValue(FrameworkElement.DataContextProperty, context);
}
Run Code Online (Sandbox Code Playgroud)
两个附加属性的替代方法是创建一个CommandBinding子类,该子类接收路由命令并激活绑定命令:
<Window.CommandBindings>
<my:CommandMapper Command="my:RoutedCommands.AddGlue" MapToCommand="{Binding AddGlue}" />
...
Run Code Online (Sandbox Code Playgroud)
在这种情况下,每个对象中的InputBindings将引用路由命令,而不是绑定.然后,此命令将路由到视图并映射.
CommandMapper的代码相对简单:
public class CommandMapper : CommandBinding
{
... // declaration of DependencyProperty 'MapToCommand'
public CommandMapper() : base(Executed, CanExecute)
{
}
private void Executed(object sender, ExecutedRoutedEventArgs e)
{
if(MapToCommand!=null)
MapToCommand.Execute(e.Parameter);
}
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute =
MapToCommand==null ? null :
MapToCommand.CanExecute(e.Parameter);
}
}
Run Code Online (Sandbox Code Playgroud)
根据我的喜好,我更愿意使用附加的属性解决方案,因为它不是很多代码并且使我不必两次声明每个命令(作为RoutedCommand和我的ViewModel的属性).支持代码只出现一次,可以在所有项目中使用.
另一方面,如果你只做一次性项目并且不希望重复使用任何东西,甚至CommandMapper也可能过度.如您所述,可以手动处理事件.
优秀的WPF框架Caliburn精美地解决了这个问题.
<TextBox cm:Message.Attach="[Gesture Key: Enter] = [Action Search]" />
Run Code Online (Sandbox Code Playgroud)
语法[Action Search]绑定到视图模型中的方法.根本不需要ICommands.
| 归档时间: |
|
| 查看次数: |
17033 次 |
| 最近记录: |