我有一个
Custom Textbox
控件,运行得很好。
public class TextBoxExt : TextBox
{
public event ehEmptyArgument ClearAll, Save;
public TextBoxExt()
{
SetResourceReference(StyleProperty, typeof(TextBoxExt));
SetValue(FocusNextControlProperty, true);
}
#region FocusNextControl
public static DependencyProperty FocusNextControlProperty =
DependencyProperty.Register("FocusNextControl", typeof(bool), typeof(TextBoxExt),
new PropertyMetadata(true, new PropertyChangedCallback(OnFocusNextControl)));
public bool FocusNextControl
{
get => Convert.ToBoolean(GetValue(FocusNextControlProperty));
set => SetValue(FocusNextControlProperty, value);
}
static void OnFocusNextControl(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBoxExt dt = d as TextBoxExt;
dt.FocusNextControl = Convert.ToBoolean(e.NewValue);
}
#endregion
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (FocusNextControl) UI.ProcessTabKey(true);
else Save?.Invoke();
}
else if (e.Key == Key.Escape) ClearAll?.Invoke();
base.OnKeyUp(e);
}
}
在这里,您可以看到 Save 和 ClearAll 是在
Key Pressed
上调用的事件,并在 XAML
表单的 C# 代码中处理。
现在我完全转向
ViewModel
并且我想避免在 XAML C#
文件中编写代码。
我怎样才能实现这个目标?
根据评论中的询问提供有助于清晰理解的附加信息
我还创建了具有相同功能的自定义控件,例如
ComboboxExt
、DateTextBox
、NumericTextBox
等
它们都用于数据输入表单,这样用户就不需要使用鼠标或Tab键来单击按钮
Save事件在整个表单的最后一个控件上触发,让后面的代码知道用户已完成所有数据的输入,并且验证和更新在
ViewModel
中处理
当用户按 escape key
取消用户所做的任何类型的修改并关闭表单时,会触发
Clear事件。这也在
ViewModel
中处理
在这里,它们有助于快速输入数据,而无需一次又一次触摸鼠标。
使自定义 WPF 控件变得对 MVVM 友好非常容易。您所要做的就是创建
ICommand
类型的 DependencyProperty
,并且 - 而不是调用 event
- 调用 ICommand.Execute
来代替:
#region ICommand SaveCommand dependency property
public static DependencyProperty SaveCommandProperty = DependencyProperty.Register(
"SaveCommand",
typeof(ICommand),
typeof(TextBoxExt),
new PropertyMetadata((ICommand)null));
public ICommand SaveCommand
{
get
{
return (ICommand)GetValue(SaveCommandProperty);
}
set
{
SetValue(SaveCommandProperty, value);
}
}
#endregion
#region ICommand ClearAllCommand dependency property
public static DependencyProperty ClearAllCommandProperty = DependencyProperty.Register(
"ClearAllCommand",
typeof(ICommand),
typeof(TextBoxExt),
new PropertyMetadata((ICommand)null));
public ICommand ClearAllCommand
{
get
{
return (ICommand)GetValue(ClearAllCommandProperty);
}
set
{
SetValue(ClearAllCommandProperty, value);
}
}
#endregion
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
if (FocusNextControl)
UI.ProcessTabKey(true);
else
this.SaveCommand?.Execute(null);
}
else if (e.Key == Key.Escape)
this.ClearAllCommand?.Execute(null);
base.OnKeyUp(e);
}
在 XAML 中,您可以以与绑定到
Button.Command
等完全相同的方式绑定到这些属性。如果您需要向命令传递更多信息,您始终可以在共享程序集中定义特定于应用程序的参数类型,并将它们作为 Execute
的参数传递给您的视图模型。
虽然这对于您自己的自定义控件非常有用,但要知道您也可以通过使用附加的命令模式对任何控件采用类似的技术,正如我在此处所述:将一个函数分配给保留 WPF 模式的 Texbox 中的 PreviewTextInput。
我还想解决那些断言您不应该在视图模型中处理此类事件并且您“必须”在视图中执行 x-y-z 的评论。这只是垃圾。没有硬性规定来规定抽象逻辑是否足够“与 UI 相关”,以至于它属于特定于平台的视图或与平台无关的视图模型。通常概念界限是模糊的,那些声称在这里给你建议的人除了“我这么说”之外没有任何可行的标准可以提供。更不幸的是,当那些主动提出意见的人甚至不尝试回答所提出的问题时,尽管你确切地知道你想要做什么以及如何实现它。我建议您遵循的唯一客观的黑白标准是:如果可以在视图模型中相对轻松和优雅地表达逻辑
独立于任何 WPF(或其他平台)特定的类型或代码,那么那就是它属于哪里。相反,如果您具有超越任何一个应用程序的特定于 UI 的逻辑和/或严重依赖于平台的具体情况(即 WPF),那么它可能最好由视图实现。如果有兴趣的话,我对这一切还有更多要说的:在 MVVM 中,可以在视图后面的代码中访问 ViewModel 吗?.
也就是说,通过接口和依赖项注入等巧妙技巧,可以以独立于平台但过于复杂的方式处理几乎所有 UI 逻辑,因此采取的建议太过分了。问题是,您是否与当前的情况作斗争,并将自己陷入逻辑困境,只是为了让代码远离人们的视线?如果是这样,那么您可能需要重新考虑事情。但我认为这里没有危险。基于视图模型的解决方案简单而优雅,并且对您的自定义
TextBox
的修改也很简单。当您使用自定义控件(顺便说一句,这很棒)时,您现在已经获得了一个 MVVM 友好的 UI 组件,可以在所有 WPF 应用程序中重用该组件,因为它不受应用程序细节的限制。如果您想将应用程序移植到另一个平台,因为您远离了混乱的单一用途 .xaml.cs 代码隐藏,您需要替换的特定于平台的代码就会少得多。这正是实践 MVVM 并最大化每一行代码价值的正确方法 - 归根结底,这是遵循任何设计模式的唯一原因。我预计这个答案至少会收到一些反对票,因为出于某种原因,我提倡的务实方法在这个网站上并不受欢迎。不过,我建议您忽略这一点,只需尝试此解决方案,并最终使用您自己的判断来构建应用程序的最佳方法。至少在我看来,你的直觉和你所走的道路都是 100% 正确的。