项目场景:
最近在使用WPF MVVM模式进行开发的时候遇到了个头大的问题,就是如何通过VM去驱动V里面的试图控件,例如最简单的通过VM里面去控制前端页面里面的文本框焦点问题,平常简单的一句话现在就感觉变得异常复杂,由于也是半路接手别人的项目,里面各种封装控件组件,遇到棘手的问题就头大。好在通过网络查找到了最终的解决方案。现做个记录,方便后期查漏补缺。
解决方案:
解决方案一:通过Messenger
类通过发布订阅的方式解决
这种方式比较易于理解,学过web前端编程的小伙伴应该都习惯用这种方式来进行数据驱动,唯一的缺点就是破坏了xmal.cs 里面无代码的环境,导致代码耦合度又变高了,不易于后期维护。而且代码不通用,无法复用到别的页面里面。
在VM里面发布消息(ViewModel.cs文件)
Messenger.Default.Send<string>("focus", "DoFocus");
在V里面订阅消息(view.xaml.cs)
public MyView()
{
InitializeComponent();
Messenger.Default.Register<string>(this, "DoFocus", doFocus);
}
public void doFocus(string msg)
{
if (msg == "focus")
this.txtcode.Focus();
}
下面这种方法扩展性更强一些,可以快速实现不通控件的焦点事件处理
解决方案二:通过扩展控件属性来完成
扩展控件属性类,里面具体包含如下方法
public static class FocusExtension
{
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusExtension), new FrameworkPropertyMetadata(IsFocusedChanged){BindsTwoWayByDefault = true});
public static bool? GetIsFocused(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool?)element.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject element, bool? value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsFocusedProperty, value);
}
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)d;
if (e.OldValue == null)
{
fe.GotFocus += FrameworkElement_GotFocus;
fe.LostFocus += FrameworkElement_LostFocus;
}
if (!fe.IsVisible)
{
fe.IsVisibleChanged += new DependencyPropertyChangedEventHandler(fe_IsVisibleChanged);
}
if ((bool)e.NewValue)
{
fe.Focus();
}
}
private static void fe_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)sender;
if (fe.IsVisible && (bool)((FrameworkElement)sender).GetValue(IsFocusedProperty))
{
fe.IsVisibleChanged -= fe_IsVisibleChanged;
fe.Focus();
}
}
private static void FrameworkElement_GotFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, true);
}
private static void FrameworkElement_LostFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, false);
}
}
我添加可见性引用的原因是标签。显然,如果您在最初可见选项卡之外的任何其他选项卡上使用附加属性,则在手动聚焦控件之前,附加属性不起作用。 另一个障碍是在失去焦点时创建一种更优雅的方法将底层属性重置为false。这就是失去焦点事件的来源。
.xaml文件里面使用
<TextBox
Text="{Binding Description}"
FocusExtension.IsFocused="{Binding IsFocused}"/>
如果有更好的方法来处理可见性问题,请告诉我。 注意:将BindsTwoWayByDefault放入DependencyProperty中。我很久以前在自己的代码中做过这个。由于此更改,WPF代码中不再需要Mode = TwoWay。