WP7 MVVM, как мне вызвать me.setfocus() из ViewModel?

Я пытаюсь улучшить свои возможности MVVM в моем следующем приложении WP7 (которое написано на Vb.NET). У меня есть текстовое поле, которому был дан фокус и отображается клавиатура WP7. Я использую привязку команд и привязываемую панель приложения Xyzzer (что отлично).

http://bindableapplicationb.codeplex.com/

Я хочу иметь возможность отменить фокус TextBox из ViewModel, установив фокус на форме. Обычно (не MVVM) я бы сделал это из формы, вызвав:

  Me.Focus()

Но я не могу сделать это из ViewModel (и я не должен). В настоящий момент я поднимаю событие из ViewModel и ловлю его на форме, но оно неприятно. Есть ли у MVVM дружественный способ сделать это? Я не использовал Инструментарий, так как есть несколько примеров в vb.net.

Я использовал команду привязки.

3 ответа

Решение

Используя пример Pedros и другие сервисы, которые я ранее реализовал в своем приложении, я собрал следующее решение в vb.net:

Создайте интерфейс IFocus, этот интерфейс может быть реализован сервисом фокуса или макетом

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 после того, как 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

Дай угадаю: проблема в том, что когда ты щелкаешь ApplicationBarIconButton, TextBox еще не обновил свойство привязки в ViewModel, верно?

Используйте ApplicationBarBehavior из Cimbalino Windows Phone Toolkit (вы также можете получить его из 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-способ решения проблемы!

Вы можете попробовать использовать поведение:

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>
Другие вопросы по тегам