1

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

http://bindableapplicationb.codeplex.com/

我希望能够通过在表单上设置焦点来从 ViewModel 中取消 TextBox 的焦点。通常(非 MVVM)我会通过调用从表单中执行此操作:

  Me.Focus()

但我不能从 ViewModel 做到这一点(我不应该这样做)。目前,我正在从 ViewModel 中提出一个事件并将其捕获到表单上,但它很讨厌。是否有一种 MVVM 友好的方式来做到这一点?到目前为止,我还没有使用过工具包,因为 vb.net 中的示例有限。

我一直在使用命令绑定。

4

3 回答 3

2

让我猜猜:问题是当您单击 ApplicationBarIconButton 时,TextBox 尚未更新 ViewModel 上的绑定属性,对吗?

使用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 解决问题的方法!

于 2012-04-15T21:26:25.123 回答
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>
于 2012-04-15T22:44:31.863 回答
0

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

创建一个IFocus接口,这个接口可以通过焦点服务或者一个Mock来实现

Public Interface IFocusInterface
    Sub Focus()
End Interface

创建一个 IFocusable 接口。这将由 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

在您的 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
于 2012-04-26T21:54:32.340 回答