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

时间:2012-04-15 21:20:38

标签: vb.net windows-phone-7 mvvm vb.net-2010 windows-phone-7.1.1

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

http://bindableapplicationb.codeplex.com/

我希望能够通过在窗体上设置焦点,从ViewModel中取消TextBox的焦点。通常(非MVVM)我会通过调用:

在表单中执行此操作
  Me.Focus()

但我无法从ViewModel中做到这一点(我不应该这样做)。目前我正在从ViewModel中提取一个事件并将其捕获在表单上,​​但它令人讨厌。有MVVM友好的方式吗?到目前为止,我还没有使用过工具包,因为vb.net中的例子有限。

我一直在使用命令绑定。

3 个答案:

答案 0 :(得分:2)

让我猜一下:问题是当你单击ApplicationBarIconButton时,TextBox还没有更新ViewModel上的绑定属性,对吗?

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

检查sample code in 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解决问题的方法!

答案 1 :(得分:2)

您可以尝试使用行为:

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>

答案 2 :(得分:0)

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

创建IFocus接口,此接口可由焦点服务或模拟

实现
Public Interface IFocusInterface
    Sub Focus()
End Interface

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

Public Interface IFocusable
    Property FocusService As IFocusInterface
End Interface

使用单例模式实现Focus接口

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

在ViewModel中实现IFocusable,并确保在构建ViewModel之后将Focus Service Singleton传入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