我們都想追求完美 Every view in the app has an empty codebehind file, except for the standard boilerplate code that calls InitializeComponent in the class's co ...
我們都想追求完美
Every view in the app has an empty codebehind file, except for the standard boilerplate code that calls InitializeComponent in the class's constructor. In fact, you could remove the views' codebehind files from the project and the application would still compile and run correctly
Josh Smith說可以刪除views' codebehind文件,程式也能正常運行。
啊...還有這麼完美的事情?那是不是views' codebehind文件里一行代碼都不寫,所有的代碼都可以寫在ViewModel里就可以了,那這樣顯示層和業務邏輯層就可以完美分離了?
理想是豐滿的,現實卻是骨幹的啊!為了追求極致的MVVM,在實際項目開發中會把自己搞的非常糾結,不知從何下手...
比如使用的第三方控制項,它不支持依賴項屬性,那我們只能把代碼寫在views' codebehind文件里。問題來了,這個控制項要使用ViewModel里的邏輯,那View怎麼訪問ViewModel的方法呢?ViewModel里要獲取這個控制項的數據,那ViewModel如何調用View里的方法呢?搜了一通,發現這個問題不好搞...
這個“老鼠屎”控制項破壞了我們實現完美MVVM的計劃,哎呀,這時候開始後悔使用MVVM了,後悔使用WPF了,乾脆使用WinForm方式開發算了,或者找個第三方MVVM框架,看能不能解決...我當初就有這種心理。
下麵通過一個簡單的demo來演示,如何解決上面的兩個問題:
View中使用ViewModel
我們在程式啟動的時候已經使用了相應的ViewModel初始化了View的DataContext
/// <summary> /// App.xaml 的交互邏輯 /// </summary> public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var vw = new MainWindow(); var vm = new MainWindowViewModel(); vw.DataContext = vm; vw.Show(); } }
因此在View中,我們可以通過DataContext獲取ViewModel
private MainWindowViewModel _vm; private void OnLoaded(object sender, RoutedEventArgs e) { if (_vm == null) { _vm = this.DataContext as MainWindowViewModel; ... } }
在View中使用ViewModel中的屬性或方法
private void LvPersons_OnSelectionChanged(object sender, SelectionChangedEventArgs e) { var item = this.LvPersons.SelectedItem as PersonModel; if (item != null) { _vm.PersonProfile = string.Format("姓名:{0}, 性別:{1}, 年齡:{2};", item.Name, item.Sex, item.Age); } }
註:可以使用blend提供的System.Window.interactivity插件,將View中事件轉換為ViewModel中命令
ViewModel中使用View
先定義一個介面
namespace MVVMSample.Comm { public interface IView { void SetAddress(Uri uri); } }
View中初始化並實現這個介面
public partial class MainWindow : IView { private void OnLoaded(object sender, RoutedEventArgs routedEventArgs) { if (_vm == null) { _vm = this.DataContext as MainWindowViewModel; if (_vm != null) { _vm.View = this; } } } ... public void SetAddress(Uri uri) { this.Wb.Navigate(uri); } }
ViewModel中定義並使用View中的介面
public IView View { private get; set; } ... public ICommand NavigationCmd { get { if (_navigationCmd == null) { _navigationCmd = new DelegateCommand(() => { if (View != null) { //IView中介面方法 View.SetAddress(new Uri("http://www.bing.com")); } }); } return _navigationCmd; } }
問題總算解決了,關鍵是,使用上述方法還能夠進行單元測試。
總結
跟業務邏輯相關的操作一定要放到ViewModel中,跟業務邏輯無關的可以放到Views' Codebehind文件里。MVVM模式是開發中的指導原則,不能強套,需要靈活使用。