首页 新闻 会员 周边 捐助

【WPF MVVM】可否在View的CodeBehind中注册Messenger用以实现UI的逻辑跳转

0
悬赏园豆:80 [已解决问题] 解决于 2015-01-19 17:07

例如:

View层内容:MainView、LoginView、DataContextView;

ViewModel层内容:MainViewModel、LoginViewModel、DataContextViewModel;

Model层内容:略;

需求:初始时MainView中包含显示LoginView,登陆成功后从MainView中移除LoginView并显示DataContextView。

可否在MainView的CodeBehind中注册一个A Messenger,A Messenger发生时将LoginView从MainView中移除,并在MainView中添加显示DataContextView,当用户登录成功时从LoginViewModel中引发A Messenger。

以上方法,导致MainView的CodeBehind需要编写一些简单的UI逻辑代码,这样是否破坏了MVVM的设计理念?如果以上方法可以被接受,那么又可否允许A Messenger携带参数,携带参数后的A Messenger导致MainView的CodeBehind中包含了少量的业务逻辑代码(不仅仅是UI逻辑代码了),如此一来是否依然遵循MVVM设计理念?

Groundnut的主页 Groundnut | 初学一级 | 园豆:130
提问于:2015-01-17 02:28
< >
分享
最佳答案
1

不行,正确的做法是这样的:

采用 Event Aggregator,在 MainViewModel 中订阅 LoginCompletedEvent 事件,在事件处理方法中移除 LoginView 并显示 DataContextView。

收获园豆:80
Launcher | 高人七级 |园豆:45050 | 2015-01-19 14:03

这样一来,MainViewModel不就知道了LoginView与DataContextView的存在吗?这样没有违背MVVM设计原则么?

Groundnut | 园豆:130 (初学一级) | 2015-01-19 16:24

@Groundnut: 我先问你,你是在什么地方装入 LoginView 的?

Launcher | 园豆:45050 (高人七级) | 2015-01-19 16:26

@Launcher: 

public partial class App : Application

{

  ……

  public void AppStartup(string[] args)
  {
    if (this.MainWindow == null)
    {
      MainView mainView = new MainView();
      mainView.DataContext = new MainViewModel();
      this.MainWindow = mainView;
    }
    this.MainWindow.Show();
  }

}

我是在APP中装入LoginView的。

Groundnut | 园豆:130 (初学一级) | 2015-01-19 16:31

@Groundnut: 我想问的是,你是在 MainView 创建之前显示的 LoginView,还是在 MainView 显示中显示的 LoginView?

Launcher | 园豆:45050 (高人七级) | 2015-01-19 16:36

@Launcher:

Hello Launcher,我是在MainView的CodeBehind中写的……

我一直感觉这样写不大稳妥,而且我发现在MVVMLight中,他们是在View中直接实例化ViewModel将其复制给View的DataContext的,但是并不是所有的ViewModel与View都有相对应,而且一些ViewModel在实例化的时候还需要一些参数。

所以我觉得直接在View中实例化并绑定ViewModel应该也不是很好的做法。那么请问,大牛你们是通过什么方法解决这些问题的?

Groundnut | 园豆:130 (初学一级) | 2015-01-19 16:44

@Groundnut: “我是在MainView的CodeBehind中写的……” -〉“这样一来,MainViewModel不就知道了LoginView与DataContextView的存在吗?”  看见没有,这些话都是你说的。按照 Prism 的指导原则,View 是需要加载到 Region 中去激活的,UI 会分成多个 Region 区,通过 Controller 实现将指定 View 加载到指定 Region 中去激活。如果你认为这个架构比较复杂,那么我再问你一个问题,DataContextView 同 MainView 是什么关系?

Launcher | 园豆:45050 (高人七级) | 2015-01-19 16:49

@Launcher: MainView包含显示LoginView与DataContextView,但是LoginView与DataContextView一次只能存在一个。此外,我还没有接触过Prism,能推荐一下怎么入门么?

Groundnut | 园豆:130 (初学一级) | 2015-01-19 16:52

@Groundnut: http://compositewpf.codeplex.com/

你也不必完全照着这个架构来做,它也有局限性,注意它的名字:Composite Application。

按照我的习惯,我会将 LoginView 拿出来,放在 new MainView(); 之前执行,这是从MFC、Winform 等传统桌面程序延续过来的做法。

对于 MainView 而言,在 Prism 中我们称它为 Shell,然后在 Shell 中划分多个不重合的 Region。我假设你的 LoginView 和 DataContextView 显示时会充满整个 Shell,那么我们在 Shell 中创建一个充满整个 Shell 的 Region,然后我们为 Shell 创建一个 Presenter,并订阅 LoginCompletedEvent,在事件处理方法中我们通过 RegionName 和 ViewName 来激活 DataContextView(DataContextView 可能存在于另一个 Module,也就是单独的 dll 中,在此 DLL 加载时会将 DataContextView 注册到我们之前创建的 Region 中去(通过 RegionName))。

Launcher | 园豆:45050 (高人七级) | 2015-01-19 17:13
其他回答(2)
0

为何要如此纠结,我的做法就是直接在codebehind里面的做,简单方便 好控制,mvvm只是一种思想,何必要那么思维定势呢

gc_Joey | 园豆:216 (菜鸟二级) | 2015-01-20 17:41

嗯,之前去查了一下Prism,后来嫌麻烦,就直接在CodeBehind中处理LoginViewModel.Logged事件了,事件传递一个Enum类型的参数,告知MainView接下来将要显示DataContextView1还是DataContextView2。CodeBehind实际并未包含任何业务逻辑,就是绑定了一个事件,然后处理一些UI逻辑跳转而已。

支持(0) 反对(0) Groundnut | 园豆:130 (初学一级) | 2015-01-20 17:47

@Groundnut: 仔细看了下,为什么你是先加载MainWindow而不是先加载Login,先加载Login其实好处多多,预加载一些,或初始化Mainwindows,这样应该更靠谱点。

顺便说下,既然Login 是MainWindow 里面的,为何不把Login做成一个View,在ViewModel里面通过属性控制隐藏显示就好了啊。

支持(0) 反对(0) gc_Joey | 园豆:216 (菜鸟二级) | 2015-01-20 17:57

@gc_Joey: Hello gc_Joey,MainView是一个窗体,内部包含一个Border(称之为Bdr_Viewer),LoginView是一个用户控件,DataContextView也是一个用户控件。

我是这样做的:在实例化MainView的时候同时实例化LoginView,并将LoginView设置为Bdr_Viewer的Child,LoginViewModel引发Logged事件时,在MainView的CodeBehind中将Bdr_Viewer的Child指向新的DataContextView。(起初MainView中显示LoggedView,之后MainView中替换显示DataContextView,而这个UI变换任务是由MainView的CodeBehind承担的。)

支持(0) 反对(0) Groundnut | 园豆:130 (初学一级) | 2015-01-20 18:23

@Groundnut: 明白了,做法感觉也没什么问题,我的做法是这样,你可以参考下。

MainWindow 的 Bdr_Viewer 里面可以包含一个Grid或者是 XXPanel,里面加入两个View(LoginView和DataContextView),MainWindowViewModel 里面分别有两个View的DataContext,LoginViewModel和DataContextViewViewModel注册一个属性来通知界面是否显示,那么我们就可以利用MainWindowViewModel来实现隐藏或者显示某个View,这样通过一个ViewModel(MainWindowViewModel) 来控制,个人认为代码的可维护性和可读性都要好很多。

支持(0) 反对(0) gc_Joey | 园豆:216 (菜鸟二级) | 2015-01-21 08:58
0

ViewModel和View之间的交互,其实可以使用Behavior和TriggerAction的,这俩都算是封装CodeBehind用的。

vbfool | 园豆:186 (初学一级) | 2015-01-21 15:16
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册