例如,我在弹出控件中有一个View的ViewModel。
ManagerView.xaml
<Popup Name="Popup1" AllowsTransparency="True" Placement="Center" PlacementTarget="{Binding ElementName=AGrid}" StaysOpen="True">
<Popup.Style>
<Style TargetType="{x:Type Popup}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsPopupVisible}" Value="True">
<Setter Property="IsOpen" Value="True"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsPopupVisible}" Value="False">
<Setter Property="IsOpen" Value="False"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Popup.Style>
<View:AssignView></View:AssignView>
</Popup>
AssignViewModel.cs:
public AssignViewModel()
{
Default = this;
}
public static AssignViewModel Default;
private ICommand iCloseCommand;
.
.
.
private void Close()
{
ManagerViewModel.Default.IsPopupVisible = false;
}
ManagerViewModel还可以访问AssignViewModel。我通过为每个ViewModel分配一个自己的静态实例来传递参数。这很有效,但当然存在依赖性问题,但这也是因为它们实际上依赖于彼此的值。
任何人都可以建议我不会有依赖的设计吗?这只是依赖于我经常遇到的另一个ViewModel的ViewModel的一个实际例子,我试图想出一种解耦它们的方法。
答案 0 :(得分:3)
嗯,第一步是删除单例模式。单身被认为是一种反模式,有几个原因超出了答案的范围。
Singleton ViewModels(至少通过静态访问器,其依赖注入容器管理的单个实例很好)违反了松散耦合的MVVM概念之一。
为了能够解耦它们,你需要控制反转(IoC)模式,最好是一个管理这种依赖关系的IoC容器(你也可以在没有IoC容器的情况下实现它,但是这样做会更难)。您将通过Constructor注入您的依赖关系链,即
public class MainViewModel
{
private readonly IMessageService messageService;
public MainViewModel(IMessageService messageService)
{
if(messageService == null)
{
throw new ArgumentNullException("messageService");
}
this.messageService = messageService;
}
private void AssignUser(int userId)
{
this.messageService.Send<AssignUserMessage>(
new AssignUserMessage()
{
UserId = userId
}
);
}
}
话虽如此,我们也来到了下一个主题,即消息服务。消息服务将是用于发布和订阅事件的服务,类似于C#/ .NET中的EventHandler
,但真正解耦。 IMessageService
只是一个例子,但有许多消息服务可用。
我个人将Prism用于私有和企业级应用程序,因为它带有MVVM所需的基础知识(带有区域支持,消息传递,绑定和通知的导航以及对WPF,Silverlight,UWP / WinPhone和Xamarin等多个平台的支持)最近)。它在开始时有一个陡峭的学习曲线,但是一旦掌握它就非常强大。
上述代码将发送一条通知消息,可以从其他ViewModel访问,即
public class AssignUserViewModel
{
private readonly IMessageService messageService;
private readonly IUserRepository users;
public AssignUserViewModel(IMessageService messageService, IUserRepository userRepository)
{
if(messageService == null)
{
throw new ArgumentNullException("messageService");
}
if(userRepository == null)
{
throw new ArgumentNullException("userRepository");
}
this.messageService = messageService;
this.users = userRepository;
// register to the AssingUserMessage here
this.messageService.Register<AssignUserMessage>(OnAssignUserMessage);
}
private void OnAssignUser(AssignUserMessage message)
{
var user = await users.GetByUserIdAsync(message.UserId);
// display your user and whatever you want to assign it and once done,
// save the changes, then send a notification that the user has been updated
this.messageService.Send<UserAssignedMessage>(
new UserAssignedMessage()
{
UserId = user.Id
}
);
}
}
这样两个ViewModel都是分离的。 MainViewModel
不了解AssignUserViewModel
的存在,反之亦然。 MainViewModel
只会发送一个通知,指出需要分配用户,AssingUserViewModel
会对其做出反应。
当事情变得更复杂时,您可能还需要一个导航服务,它将导航(切换视图或打开一个新窗口等)到视图并将所需的参数传递给它,但那是另一个话题。您通常会将您的导航服务注入到与消息服务相同的位置。
有关如何使用导航服务的示例,请查看我的其他回复here和here。
MVVM可能非常复杂,只要您使用单个View超越单个ViewModel,因为大多数关于此主题的教程都限于此。
答案 1 :(得分:0)
保持单向数据流。 IsPopupVisible可以映射到Model中的某个状态,ManagerViewModel可以从Model中侦听状态更改并更改自己的属性,而AssignViewModel可以更改共享模型的状态。
答案 2 :(得分:-1)
您可以使用Activity
模式。
public class PopupResult
: IViewModel
{
//Some code
}
public class ActivityRunner
{
public Task<PopupResult> Do<PopupResult>()
{
var tcs = new TaskCompletionSource<PopupResult>();
var view = new Popup();
var model = new PopupResult();
view.DataContext = model;
view.Close += (e, o) => tcs.SetResult(model);
return tcs.Task;
}
}
async void ShowPopupThenDoSomething()
{
PopupResult result = await IActivityRunner.Do<Popup>();
//Do something when we close
}