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>