我在项目中使用ViewModelLocator。
我在app.xaml中的ResourceDictionary中执行ViewModelLocator的BindingContent
我的代码:
的App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controlsHelpers="clr-namespace:MyProj.Source.Helpers.UIHelper.ControlsHelpers;assembly=MyProj"
xmlns:configuration="clr-namespace:MyProj.Source.Configuration;assembly=MyProj"
x:Class="MyProj.App">
<Application.Resources>
<ResourceDictionary>
<configuration:ViewModelLocator x:Key="ViewModelLocator"/>
</ResourceDictionary>
</Application.Resources>
</Application>
ViewModelLocator.cs
public class ViewModelLocator
{
public LoginViewModel LoginViewModel { get; set; }
public SignUpViewModel SignUpViewModel { get; set; }
public ViewModelLocator()
{
LoginViewModel = new LoginViewModel();
SignUpViewModel = new SignUpViewModel();
}
}
LoginViewModel.cs
public class LoginViewModel : AbstractViewModel
{
private MyModel UserData { get; set; } = new MyModel();
private bool _isSignRequired;
public bool IsSignRequired
{
get { return _isSignRequired; }
set
{
if (value == _isSignRequired) return;
_isSignRequired = value;
OnPropertyChanged();
}
}
public string UserName
{
get { return UserData.UserName; }
set
{
if (value == UserData.UserName) return;
UserData.UserName = value;
OnPropertyChanged();
}
}
public string Password
{
get { return UserData.Password; }
set
{
if (value == UserData.Password) return;
UserData.Password = value;
OnPropertyChanged();
}
}
}
SignUpViewModel.cs
public class SignUpViewModel : AbstractViewModel
{
private string _checkUserResultImage;
public MyModel UserData { get; set; } = new MyModel();
public string Phone
{
get { return UserData.Phone; }
set
{
if (UserData.Phone != null && value == UserData.Phone) return;
UserData.Phone = value;
OnPropertyChanged();
}
}
public string UserName
{
get { return UserData.UserName; }
set
{
if (UserData.UserName != null && value == UserData.UserName) return;
UserData.UserName = value;
OnPropertyChanged();
}
}
public string Password
{
get { return UserData.Password; }
set
{
if (UserData.Password != null && value == UserData.Password) return;
UserData.Password = value;
OnPropertyChanged();
}
}
public string Email
{
get { return UserData.Email; }
set
{
if (UserData.Email != null && value == UserData.Email) return;
UserData.Email = value;
OnPropertyChanged();
}
}
public string CheckUserResultImage
{
get { return _checkUserResultImage; }
set
{
if (value == _checkUserResultImage) return;
_checkUserResultImage = value;
OnPropertyChanged();
}
}
public Command SignUpCommand
{
get
{
return new Command(async () =>
{
//Here I want to get data field from LoginViewModel
});
}
}
}
我想从SignUpViewModel中的LoginViewModel获取数据。
我该怎么办?
答案 0 :(得分:3)
你的问题比我想象的更为微妙和复杂。
您问“我如何从LoginViewModel获取信息到我的SignupViewModel?”有很多不同的方法来做到这一点!遗憾的是,其中许多都很糟糕,违背了MVVM设计模式的目的。 MVVM鼓励组件解耦和封装。
做你想做的最明显,(也是最差)的方法是简单地在SignupViewModel中获取对LoginViewModel的引用,并直接引用它的属性。不幸的是,这是一个非常脆弱的解决方案,并对您的LoginViewModel强制执行硬依赖。如果您的登录流程发生变化,您的SignupViewModel必须随之更改。不理想。
因此,为了避免组件紧密耦合,您需要做一些更简单的事情:仅传递您感兴趣的数据。有很多方法可以做到这一点,例如事件,参数传递或消息传递系统。
事件工作正常,但我建议不要这样做,因为它会再次强制你的SignupViewModel直接依赖你的LoginViewModel。如果你对它没问题,那就可以了。
参数传递可能是最轻量级的解决方案,但不幸的是,Xamarin Forms不支持开箱即用。我之前已经实现了它,但它涉及一些工作。
允许您订阅和发布任意消息的消息传递系统是一种非常常见的解决方案(事实上,事件实际上是一种特定的形式)。如果您正在寻找一个快速插入式解决方案,我认为这可能是您最好的选择,因为MVVM Light Toolkit附带了一个完全相同的Messenger。即使你没有使用那个确切的实现,但从根本上说,你会想要做这样的事情:
让我们说你的Messenger实现了某种类似的接口:
public interface IMessenger
{
Publish<TMessage>(TMessage);
Subscribe<TMessage>(object, Action<TMessage>); //the object here should be a reference to the recipient.
}
然后您的实现可能如下所示:
您可以使用此课程传递信息:
public class LoginMessageArgs
{
public string Username {get; private set;}
//whatever other information this message needs to contain...
}
您的LoginViewModel:
public class LoginViewModel : AbstractViewModel
{
//all your properties go here...
IMessenger messengerReference;
public LoginViewModel()
{
//Get a reference to your Messenger somehow. Maybe it's a singleton in ViewModelLocator?
messengerReference = ViewModelLocator.Messenger;
}
//Maybe you call this method when you navigate away from the LoginViewModel, or whenever it makes sense to send this information to your SignupViewModel.
private void PassLoginInformation()
{
messengerReference.Publish<LoginMessageArgs>(new LoginMessageArgs { Username = this.Username }); //etc
}
}
您的SignupViewModel:
public class SignUpViewModel : AbstractViewModel
{
//all your properties go here...
public SignupViewModel()
{
//Get a reference to your Messenger somehow. Maybe it's a singleton in ViewModelLocator?
IMessenger messengerReference = ViewModelLocator.Messenger;
messenger.Register<LoginMessageArgs>(this, OnLoginMessageReceived);
}
private OnLoginMessageReceived(LoginMessageArgs message)
{
//Do stuff with your message
}
}
呼!希望这会有所帮助。
答案 1 :(得分:0)
我找到解决方案来解决我的问题。但我不确定这是好方法。
我将BindingContext添加到了app.xaml
<Application.BindingContext>
<x:StaticResource Key="ViewModelLocator"/>
</Application.BindingContext>
我可以访问ViewModelLocator和LoginViewModel。
var model=Application.Current.BindingContext as ViewModelLocator;
var login=model?.LoginViewModel.UserName;
这是好的做法吗?