2009-01-08 37 views
16

我一直在開發一個非常大的LOB應用程序,使用我稱之爲M-V-MC(Model-View-ModelController)的M-V-VM的味道,這是M-V-C和M-V-VM之間的一種組合。我發佈了this answer關於視圖如何在M-V-VM中實例化到「what-are-the-most-common-mistakes-made-in-wpf-development」這個問題。M-V-VM - 在ViewModel中使用命令的任何示例?

Sam作了如下評論關於我的回答:

這將創建一個跟進-問題: 你如何創建視圖?我使用 RelayCommands綁定動作從 視圖到ViewModel,所以視圖 甚至不知道一個動作有 解僱,不知道他應該打開一個 新視圖。解決方案:在 中爲View創建一個事件來訂閱?

當我最初開始M-V-VM發展我有這個想法,一切都應該生活在視圖模型,並研究了大量的例子,從男人喜歡Josh SmithKarl Shifflett。然而,我還沒有想出一個很好的例子,當一個命令需要在ViewModel中生存時。

例如,假設我有一個顯示客戶的ListView和一個單擊的按鈕,以允許我編輯當前選定的客戶。 ListView(View)綁定到CustomerVM(ViewModel)。單擊該按鈕將觸發EditCustomerCommand,它將打開一個彈出窗口,該窗口允許我編輯CustomerVM的所有屬性。這個EditCustomerCommand在哪裏居住?如果涉及到打開一個窗口(UI功能),不應該在視圖的代碼隱藏中定義它嗎? alt text

有沒有人有任何例子,當我應該在視圖與ViewModel中定義一個命令?下面

Matthew Wright狀態:

新的和刪除列表中會 很好的例子。在這些情況下,將添加一個空白 記錄或ViewModel刪除當前記錄 。任何 視圖採取的行動應該在 響應這些事件發生。

所以,如果我點擊新按鈕,會發生什麼? CustomerVM的一個新實例由Parent ViewModel創建並添加到它的收集權限中?那麼我的編輯屏幕如何打開?該視圖應該創建Customer ViewModel的新實例,並將它傳遞給ParentVM.Add(newCreatedVM)方法嗎?

比方說,我通過生活在虛擬機上的DeleteCommand刪除客戶記錄。 VM調用業務層並嘗試刪除記錄。它不能這樣,它會向VM返回一條消息。我想在對話框中顯示此消息。視圖如何從命令操作中獲取消息?

回答

3

從來沒有想過我會看到自己被引用的問題。

我思考這個問題,我有一段時間了,並取得了相當務實的決定對我的代碼庫:

在我的代碼基礎,當動作發生的視圖模型獲取調用,並且我希望它保持這樣。另外我不希望ViewModel控制視圖。

我做了什麼?
我加了一個控制器導航:

public interface INavigation 
{ 
    void NewContent(ViewModel viewmodel); 
    void NewWindow(ViewModel viewmodel); 
} 

該控制器也包含兩個動作:NewContent()並顯示在當前窗口的新內容,NewWindow()創建一個新的窗口,與內容填充它,顯示它。
當然,我的viewmodels不知道要顯示哪個視圖。但是他們確實知道他們想要展示哪個視圖模型,所以根據你的例子,當DeleteCommand被執行時,它會調用導航服務函數NewWindow(new ValidateCustomerDeletedViewModel())來顯示一個窗口,指出'客戶已被刪除'(對這個簡單的消息框過度殺傷,但對於簡單的消息框會有一個特殊的導航功能)。

viewmodel如何獲得導航服務?

我的視圖模型的類有該導航控制器的屬性:

public class ViewModel 
{ 
    public INavigation Navigator { get; set; } 
    [...] 
} 

當視圖模型被連接到一個窗口(或任何顯示視圖),則窗口將設置導航屬性,所以視圖模型可以調用它。

導航器如何創建viewmodel的視圖?

你可以有一個簡單的列表,它查看創建該視圖模型,在我的情況,我可以,因爲名稱匹配使用簡單的反射:

public static FrameworkElement CreateView(ViewModel viewmodel) 
{ 
    Type vmt = viewmodel.GetType(); 
    // big bad dirty hack to get the name of the view, but it works *cough* 
    Type vt = Type.GetType(vmt.AssemblyQualifiedName.Replace("ViewModel, ", "View, ")); 
    return (FrameworkElement)Activator.CreateInstance(vt, viewmodel); 
} 

當然認爲需要一個構造函數接受視圖模型作爲參數:

public partial class ValidateCustomerDeletedView : UserControl 
{ 
    public ValidateCustomerDeletedView(ValidateCustomerDeletedViewModel dac) 
    { 
    InitializeComponent(); 
    this.DataContext = dac; 
    } 
} 

我的窗口是怎樣的?

簡單:我的主窗口實現了INavigation接口,並顯示了創建的初始頁面。見自己:

public partial class MainWindow : Window, INavigation 
{ 
    public MainWindow() 
    { 
    InitializeComponent(); 
    NewContent(new StartPageViewModel()); 
    } 

    public MainWindow(ViewModel newcontrol) 
    { 
    InitializeComponent(); 
    NewContent(newcontrol); 
    } 

    #region INavigation Member 
    public void NewContent(ViewModel newviewmodel) 
    { 
    newviewmodel.Navigator = this; 
    FrameworkElement ui = App.CreateView(newviewmodel); 
    this.Content = ui; 
    this.DataContext = ui.DataContext; 
    } 

    public void NewWindow(ViewModel viewModel) 
    { 
    MainWindow newwindow = new MainWindow(viewModel); 
    newwindow.Show(); 
    } 
    #endregion 
} 

(這一點與NavigationWindow同樣出色的工作和包裝視圖插入到頁面中)

當然,這是可測試的,因爲導航控制器可以很容易地嘲笑。

我不確定這是否是一個完美的解決方案,但它現在對我來說很好。任何想法和意見,歡迎!

0

新的和從列表中刪除將是很好的例子。在這些情況下,添加一個空白記錄或當前記錄由ViewModel刪除。視圖採取的任何行動應該是針對這些事件發生的。

+0

所以,如果我點擊新按鈕,會發生什麼?該視圖應該創建一個ViewModel的新實例,並將它傳遞給CurrentVM.Add(newCreatedVM),但對嗎? – Micah 2009-01-08 17:16:40

+0

假設我通過VM上的DeleteCommand刪除客戶記錄。 VM調用業務層並嘗試刪除記錄。它不能這樣,它會向VM返回一條消息。我想在對話框中顯示此消息。視圖如何從命令操作中獲取消息? – Micah 2009-01-08 17:30:40

0

一種方法是使用業務層可以修改的命令參數對象,並且VM可以在命令執行後處理。

1

對於你的刪除消息框的情況,我通過一個接口抽象出消息框。與此類似。我也爲我的WPF應用程序注入這些接口。

構造

public MyViewModel(IMessage msg) 
    { 
     _msg = msg; 
    } 

然後,在方法刪除方法在視圖模型...像

public void Delete() 
    { 
     if(CanDelete) 
     { 
     //do the delete 
     } 
     else 
     { 
     _msg.Show("You can't delete this record"); 
     } 
    } 

這將使它可檢驗的,你可以在不同的即時聊天插件實現唐·實際上並不顯示消息框。這些可能只是打印到控制檯,用於測試目的。顯然,你的WPF應用程序可能有這樣

public class MessageBoxQuestion : IMessage 
{ 
    public void Show(string message) 
    { 
    MessageBox.Show(message); 
    } 
} 

這樣做可以使測試不同的途徑實現(認爲是/否對話框)非常簡單和直接的。你可以想象一個刪除確認。您可以使用IMessage的具體實例返回true/false進行確認,也可以在測試期間模擬容器。

[Test] 
public void Can_Cancel_Delete() 
{ 
    var vm = new ProductViewModel(_cancel); 
    ... 

} 
[Test] 
public void Can_Confirm_Delete() 
{ 
    var vm = new ProductViewModel(_yes); 
    ... 

} 

關於何時使用Command的其他問題,我從有問題的視圖中實例化了Add New或Details視圖。就像你在你的例子中一樣。 視圖僅在我們的應用中由其他視圖實例化。在這些情況下,我不使用命令。但是,我會將父視圖的ViewModel屬性用於子視圖。

public void Object_DoubleClick(object sender, EventArgs e) 
{ 
    var detailView = new DetailView(ViewModel.Product); 
    detailView.Show(); 
} 

希望這會有所幫助!

相關問題