WP7 MVVM 如何从 ViewModel 调用 me.setfocus()



我正在尝试在我的下一个WP7应用程序(用 Vb.NET 编写)中提高我的MVVM能力。 我有一个文本框,该文本框已获得焦点并显示WP7键盘。 我正在使用命令绑定和 xyzzer 的可绑定应用程序栏(这非常好)。

http://bindableapplicationb.codeplex.com/

我希望能够通过在窗体上设置焦点来从视图模型中取消文本框的焦点。 通常(非 MVVM)我会通过调用以下命令从表单中执行此操作:

  Me.Focus()

但是我不能从视图模型执行此操作(我不应该这样做)。 目前,我正在从ViewModel中引发一个事件并在表单上捕获它,但它令人讨厌。 有没有 MVVM 友好的方法来做到这一点? 到目前为止,我还没有使用过工具包,因为 vb.net 中的例子有限。

我一直在使用命令绑定。

您可以尝试使用以下行为:

public class FocusBehavior : Behavior<Control>
    {
        protected override void OnAttached()
        {
            AssociatedObject.GotFocus += (sender, args) => IsFocused = true;
            AssociatedObject.LostFocus += (sender, a) => IsFocused = false;
            AssociatedObject.Loaded += (o, a) => { if (HasInitialFocus || IsFocused) AssociatedObject.Focus(); };
            base.OnAttached();
        }
        public static readonly DependencyProperty IsFocusedProperty =
            DependencyProperty.Register(
                "IsFocused",
                typeof (bool),
                typeof (FocusBehavior),
                new PropertyMetadata(false, (d, e) => { if ((bool) e.NewValue) ((FocusBehavior) d).AssociatedObject.Focus(); }));
        public bool IsFocused
        {
            get { return (bool) GetValue(IsFocusedProperty); }
            set { SetValue(IsFocusedProperty, value); }
        }
        public static readonly DependencyProperty HasInitialFocusProperty =
            DependencyProperty.Register(
                "HasInitialFocus",
                typeof (bool),
                typeof (FocusBehavior),
                new PropertyMetadata(false, null));
        public bool HasInitialFocus
        {
            get { return (bool) GetValue(HasInitialFocusProperty); }
            set { SetValue(HasInitialFocusProperty, value); }
        }
    }

然后在 xaml 中:

 <TextBox>
            <i:Interaction.Behaviors>
                <behaviors:FocusBehavior HasInitialFocus="True"
                                         IsFocused="{Binding IsFocused}" />
            </i:Interaction.Behaviors>
        </TextBox>

让我猜猜:问题是当您单击应用程序栏图标按钮时,文本框尚未更新视图模型上的绑定属性,对吗?

使用来自 Cimbalino Windows Phone Toolkit 的 ApplicationBarBehavior(您也可以从 NuGet 获取它),它在内部处理它 - 因此在 ApplicationBarIconButton 单击事件完成之前,它已经更新了 TextBox.Text 绑定属性!

检查 GitHub 中的示例代码,您就可以使用它了!

编辑:

如果您只想在页面上设置焦点(从而在 TextBox 失去焦点后关闭键盘),我会使用外部类来完成这项工作,然后在 ViewModel 中使用它,如下所示:

//This is the service interface
public interface IPageService
{
    void Focus();
}
//This implements the real service for runtime 
public class PageService : IPageServiceusage
{
    public void Focus()
    {
        var rootFrame = Application.Current.RootVisual as PhoneApplicationFrame;
        if (rootFrame == null)
            return;
        var page = rootFrame.Content as PhoneApplicationPage;
        if (page == null)
            return;
        page.Focus();
    }
}
//This implements the mockup service for testing purpose
public class PageServiceMockup : IPageService
{
    public void Focus()
    {
        System.Diagnostics.Debug.WriteLine("Called IPageService.Focus()");
    }
}

然后,在 ViewModel 上,创建一个如下所示的服务实例:

public class MyViewModel
{
    private IPageService _pageService;
    public MyViewModel()
    {
#if USE_MOCKUP
        _pageService = new PageServiceMockup();
#else
        _pageService = new PageService();
#endif
    }
}

当您想在页面上设置焦点时,您所要做的就是调用 _pageService.Focus() .

这是解决问题的完全 MVVM 方法!

使用 Pedros 示例以及我之前在我的应用程序中实现的其他服务,我在 vb.net 中将以下解决方案组合在一起:

创建一个 IFocus 接口,此接口可以由焦点服务或模拟实现

Public Interface IFocusInterface
    Sub Focus()
End Interface

创建 IFocable 接口。 这将由 ViewModel 实现,并接受实现 IFocusInterface 的对象。

Public Interface IFocusable
    Property FocusService As IFocusInterface
End Interface

使用单例模式实现焦点接口

Imports Microsoft.Phone.Controls
Public NotInheritable Class FocusService
    Implements IFocusInterface
    Private Sub New()
    End Sub
    Private Shared ReadOnly m_instance As New FocusService
    Public Shared ReadOnly Property Instance() As FocusService
        Get
            Return m_instance
        End Get
    End Property
    Public Sub Focus() Implements IFocusInterface.Focus
        Dim rootFrame = TryCast(Application.Current.RootVisual, PhoneApplicationFrame)
        If Not rootFrame Is Nothing Then
            Dim page = TryCast(rootFrame.Content, PhoneApplicationPage)
            If Not page Is Nothing Then
                page.Focus()
            Else
                Throw New Exception("Unable to Cast the Root Frame Content into an Application Page")
            End If
        Else
            Throw New Exception("Unable to Cast the RootVisual into a PhoneApplicationFrame")
        End If
    End Sub
End Class

在视图模型中实现 IFocusable,并确保在构造视图模型后将焦点服务单一实例传递到 ViewModel。

Public Class MyViewModel
    Implements INotifyPropertyChanged
    Implements IFocusable
    ' Property for the Focus Service
    <Xml.Serialization.XmlIgnore()> Public Property FocusService As IFocusInterface Implements IFocusable.FocusService
    Public Sub Focus()
        If Not FocusService Is Nothing Then
            FocusService.Focus()
        Else
            Throw New Exception("ViewModel hasn't been passed a Focus Service")
        End If
    End Sub
End Class
Dim tMyViewModel as New MyViewModel
tMyViewModel.FocusService = Vacation_Calc_Model.FocusService.Instance

最新更新