2014-04-16 21 views
0

我開始在現有的WPF c#應用程序中實現MVVM設計模式。我是全新的,從未使用過設計模式或依賴注入。我正在研究已經可用的框架並採用MVVM光源。我將視圖中的邏輯移至視圖模型。我在PopulateTestMenu中有很多與視圖模型中的UI相關的代碼。它也有對事件處理程序的調用。我該如何照顧這件事?MVVM - View模型中的UI相關代碼 - 關注的真正分離

XAML我:

<Window DataContext="{Binding Main, Source={StaticResource Locator}}"> 
    <Menu> 
     <MenuItem Header="Load All History..." Command="{Binding LoadAllHistory}"> 

MainViewModel I類有:

public ICommand LoadAllHistory { get; private set; } 

public MainViewModel() 
{    
    LoadAllHistory = new RelayCommand(() => LoadHistoryExecute(),() => true); 
} 

,我從我的觀點移到視圖模型代碼:

private void LoadHistoryExecute() 
{ 
    try 
    { 
     OpenFileDialog ofd = new OpenFileDialog(); 
     ofd.Filter = "Test History File (*.xml)|*.xml"; 
     ofd.Title = "Open Test History"; 
     ofd.Multiselect = true; 

     if (ofd.ShowDialog() == true) 
     { 
      ThreadPool.QueueUserWorkItem(LoadTestHistoryCallback, ofd.FileNames); 
     } 
    } 
    catch 
    { 
     //some code 
    } 
} 

private void LoadTestHistoryCallback(object state) 
{ 
    try 
    { 
     string[] fileNames = (string[])state; 

     foreach (string fileName in fileNames) 
     { 
      bool success = MyApp.Instance.ParseTestHistory(fileName); 
      string status = success 
          ? String.Format("'{0}' loaded successfully.",  
       System.IO.Path.GetFileName(fileName)) 
          : String.Format("Failed to load history from '{0}'.",  
       System.IO.Path.GetFileName(fileName)); 
      Dispatcher.CurrentDispatcher.DynamicInvoke(delegate() 
      { 
       Status = status; 
      }); 
      PopulateTestMenu(new SortedList<int, int>()); 
     } 
    } 
    catch 
    { 
     //some code 
    } 
} 

private void PopulateTestMenu(SortedList<int, int> indexes) 
{ 
    try 
    { 
     _testMenuMutex.WaitOne(); 

     //Populate the Tests menu with the list of tests. 
     Dispatcher.CurrentDispatcher.DynamicInvoke(delegate() 
     { 
      menuTests.Items.Clear(); 
      var checkEventHandler = new RoutedEventHandler(testMenuItem_Checked); 
      bool added = false; 

      if (MyApp.Instance.TestHistory != null && 
       MyApp.Instance.TestHistory.Count > 0) 
      { 
       List<ushort> subIds = new 
        List<ushort>MyApp.Instance.TestHistory.Keys); 

       foreach (ushort subId in subIds) 
       { 
        MenuItem menuItem = null; 
        menuItem = new MenuItem(); 
        menuItem.Header = subId.ToString().PadLeft(5, '0');** 

        MenuItem none = new MenuItem(); 
        none.Header = "None"; 
        none.IsCheckable = true; 
        none.IsChecked = true; 
        none.Checked += checkEventHandler; 
        none.Unchecked += checkEventHandler; 

        menuItem.Items.Add(none); 

        if (MyApp.Instance.TestHistory != null && 
         MyApp.Instance.TestHistory.ContainsKey(subId)) 
        { 
         var tests = MyApp.Instance.TestHistory[subId]; 

         if (tests != null) 
         { 
          foreach (Test t in tests) 
          { 
           MenuItem item = new MenuItem(); 
           item.IsCheckable = true; 

           string description = t.Description.Replace("\n", 
           "\n".PadRight(34, ' ')); 
           string header = abc; 
           item.Header = header; 
           item.DataContext = t; 
           item.Checked += checkEventHandler; 
           item.Unchecked += checkEventHandler; 
           menuItem.Items.Add(item); 
          } 
          if (tests.Count > 0) 
          { 
           menuTests.Items.Add(menuItem); 
           added = true; 
          } 
         } 
        } 

        // Carry over the previous selection. 
        if (indexes.ContainsKey(subId) && indexes[subId] > -1) 
        {       ((MenuItem)menuItem.Items[indexes[subId]]).IsChecked = 
         true; 
        } 
       } 
      } 
+2

直視的項目綁定,你可以只綁定物品,而不是由一個 – Steve

+1

編號A VM填充在一個菜單的列表將不會被創建的MenuItems,因爲它打破了抽象的原則和涉及的具體對象UI表面。 VM可以創建綁定到菜單的項目集合。在Xaml中創建一個菜單並將其ItemsSource屬性綁定到一個集合! –

回答

0

通過將該代碼移動到ViewModel,您已經正確應用了MVVM的理論,但請記住,該視圖只應提供顯示的「結構」。

顯示內容由ViewModel中的模型提供。考慮到這一點,從ViewModel方法中分離出菜單部分並將它們放入View中,但保留Test對象創建部件(將ViewModel對象綁定到View結構就是它的意思)。

在你的PopulateTestMenu方法中,需要在View中指定菜單和菜單結構,而填充它們的數據需要在ViewModel中創建和格式化。

在視圖中,您將將適當的對象部件綁定到菜單結構,並且當模型綁定到視圖時,ViewModel將自動用模型對象填充它。

查看代碼,看起來您的Test對象是您的ViewModel,並且需要在View中創建Menu和MenuItem結構,然後指定Test對象的特定屬性與特定結構的綁定視圖中的部分菜單。

+0

當用戶點擊菜單項menuTests的items.clear()被調用,併爲testMenuItemChecked一個新的事件處理程序被實例化。我希望我的視圖的代碼沒有任何事件處理程序。我想用命令來綁定所有的東西。你能解釋一下我如何修改PopulateTestMenu()中的代碼來實現你剛纔所說的嗎?謝謝。 – Asha

+1

@brinky - > MenuItem位於控件名稱空間中。你真的想在虛擬機中依賴嗎? –

+0

你絕對不想在視圖模型中引用MenuItem。 – BenjaminPaul

2

我仍然在試圖找出你所要求的=)...

但你是混合了一些東西......還記得MVVM的核心概念之一是使視圖模型可測試和刪除所有從視圖模型中查看相關的代碼。所以根本沒有依賴WPF。所以MenuItem看起來像WPF MenuItem,不應該在ViewModel

相反,您可以考慮製作一個MenuItemViewModel,它與視圖中的MenuItem綁定。我可以看到一個ObservableCollection<MenuItemViewModel> TestMenu而不是你的排序列表。

在你的方法LoadTestHistoryCallback你會instanciate(可以通過DI完成)MenuItemViewModel並將其添加到TestMenu集合。 MenuItemViewModel可能有status財產可以從外部或內部分配。 (它也可以有一些額外的邏輯,嘿它是一個視圖模型)。

View中,您可以通過DataBinding將其綁定到一個帶有代表MenuItem的模板的列表。

<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" /> 

所以請記住ViewModel也可以包含ViewModelsviewmodelcollections。 使用WPF中豐富的數據綁定api。 工作與可綁定屬性,如ObservebaleCollectionsProperties被擴展爲PropertyChanged通知。

HTH

PS:然後您可以點擊ICommandMenuItemViewModel和執行動作或更好的使用EventAggregatorMessenger通知其他ViewModels ...(但是這是一個故事的另一個問題=).. 。)

+0

非常有幫助的評論。我將在我的MainViewModel中創建一個MenuItemViewModel,並看看我可以如何做itemBinding。 – Asha

+0

很高興我可以幫助=)...如果這回答你的問題考慮將其標記爲'answerd' – silverfighter