2014-01-13 61 views
2

我確定我在這裏錯過了一些非常愚蠢和愚蠢的事情,當我看到它時可能會踢自己,但我只是有一個簡單的問題。從ViewModel訪問MVVM設計模式中的this.content

我有在後臺代碼與網格視圖的構造函數中的一些代碼,即執行以下操作:

Grid mainGrid = this.Content as Grid; 
MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem; 
this.ApplySkinFromMenuItem(item); 

所以我的問題是我怎麼能做到這一點從視圖模型? ViewModel不知道「this」是什麼,也沒有對「this」的引用。

這是我的理解是,視圖模型對象在XAML創建致電:

<ObjectDataProvider x:Key="TimersHostViewModel" ObjectType="{x:Type local:TimersHostViewModel}"/> 

和設置像這樣的數據上下文:

<Grid DataContext="{StaticResource TimersHostViewModel}" Style="{DynamicResource styleBackground}"> 

但是,這並沒有給TimersHostViewModel關於「this.Content」的任何知識,並說TimersHost.Content沒有幫助,因爲TimersHost不是一個實際的對象,而是一個類,我需要一個實際的對象來獲得「.Content」,它應該是正確的對象,是來自代碼背後的對象,但我怎麼能那麼進入視圖模型?

畢竟以下的MVVM意味着ViewModel不應該有任何關於View的知識,View不應該有任何關於ViewModel的知識,而且他們只是通過綁定和INotifyPropertyChanged和其他類似的消息來回傳遞傳球技術。我在另一個應用程序中完成了一些這樣的工作,所以我對某些基本知識很熟悉,但仍然是一些新知識,仍然在學習甚至重新學習。

我已經在下面列出了全部內容。正如你所看到的,我正在試圖將代碼從代碼中移出並放到ViewModel中,但是當我試圖從主網格獲取this.Content時遇到了編譯器錯誤。

XAML:

<Window 
x:Class="TimersXP.TimersHost" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase" 
xmlns:local="clr-namespace:TimersXP" 
Name="TimersHostView" 
SizeToContent="Height" 
Title="TimersXP" 
WindowStartupLocation="CenterScreen" 
WindowStyle="ToolWindow"> 
<Window.Resources> 
    <ObjectDataProvider x:Key="TimersHostViewModel" ObjectType="{x:Type local:TimersHostViewModel}"/> 
</Window.Resources> 

<Grid.RowDefinitions> 
    <RowDefinition Height="21"/> 
    <RowDefinition Height="*" /> 
</Grid.RowDefinitions> 
<Grid.ColumnDefinitions> 
    <ColumnDefinition Width="*"/> 
    <ColumnDefinition Width="90"/> 
</Grid.ColumnDefinitions> 

<!--Main Menu--> 
<Menu IsMainMenu="True" Style="{DynamicResource styleBanner}" Margin="0,0,0,1"> 
    <MenuItem Header="_Help" Style="{DynamicResource styleBanner}"> 
     <MenuItem Header="_About" Style="{DynamicResource styleBanner}"/> 
    </MenuItem> 
</Menu> 

<!--Top Most Check Box--> 
<CheckBox Content="Top Most" Grid.Column="1" Height="16" HorizontalAlignment="Left" Margin="11,2,0,0" Name="checkBox1" VerticalAlignment="Top" /> 

<!--Stopwatch & Countdown Tab Defintions--> 
<TabControl Grid.Row="1" Grid.ColumnSpan="2" Style="{DynamicResource styleContentArea}"> 
    <TabItem Header="Stopwatch"/> 
    <TabItem Header="Countdown"/> 
</TabControl> 

<!-- CONTEXT MENU --> 
<Grid.ContextMenu> 
    <ContextMenu Style="{DynamicResource styleBanner}" MenuItem.Click="OnMenuItemClick"> 
    <MenuItem Tag=".\Resources\Skins\BlackSkin.xaml" IsChecked="True"> 
     <MenuItem.Header> 
     <Rectangle Width="120" Height="40" Fill="Black" /> 
     </MenuItem.Header> 
    </MenuItem> 
    <MenuItem Tag=".\Resources\Skins\GreenSkin.xaml"> 
     <MenuItem.Header> 
     <Rectangle Width="120" Height="40" Fill="Green" /> 
     </MenuItem.Header> 
    </MenuItem> 
    <MenuItem Tag=".\Resources\Skins\BlueSkin.xaml"> 
     <MenuItem.Header> 
     <Rectangle Width="120" Height="40" Fill="Blue" /> 
     </MenuItem.Header> 
    </MenuItem> 
    </ContextMenu> 
</Grid.ContextMenu> 
</Grid> 
</Window> 

代碼背後:

using System; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Diagnostics; 
using System.Windows; 
using System.Windows.Controls; 

namespace TimersXP 
{ 
public partial class TimersHost : Window 
{ 
    public TimersHost() 
    { 
     try 
     { 
      InitializeComponent(); 
     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine("CTOR Exception: " + ex.Message); 
     } 

     // Load the default skin. 
     Grid mainGrid = this.Content as Grid; 
     MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem; 
     this.ApplySkinFromMenuItem(item); 

    } 

    public void OnMenuItemClick(object sender, RoutedEventArgs e) 
    { 
     MenuItem item = e.OriginalSource as MenuItem; 

     // Update the checked state of the menu items. 
     //Grid mainGrid = this.Content as Grid; 
     //foreach (MenuItem mi in mainGrid.ContextMenu.Items) 
     //mi.IsChecked = mi == item; 

     // Load the selected skin. 
     this.ApplySkinFromMenuItem(item); 
    } 

    void ApplySkinFromMenuItem(MenuItem item) 
    { 
     // Get a relative path to the ResourceDictionary which 
     // contains the selected skin. 
     string skinDictPath = item.Tag as string; 
     Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative); 

     // Tell the Application to load the skin resources. 
     App app = Application.Current as App; 
     app.ApplySkin(skinDictUri); 
    } 
} 
} 

視圖模型:

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.Windows; 
using System.Windows.Controls; 

namespace TimersXP 
{ 
public class TimersHostViewModel 
{ 
    public TimersHostViewModel() 
    { 
     // Load the default skin. 
     Grid mainGrid = this.Content as Grid; <---- ERROR HERE 
    } 
    //public void TimersHostViewModel() 
    //{ 
    // // Load the default skin. 
    // Grid mainGrid = TimersHost.Content as Grid; 
    // MenuItem item = mainGrid.ContextMenu.Items[0] as MenuItem; 
    // //this.ApplySkinFromMenuItem(item); 
    //} 

    public void OnMenuItemClick(object sender, RoutedEventArgs e) 
    { 
     MenuItem item = e.OriginalSource as MenuItem; 

     // Update the checked state of the menu items. 
     //Grid mainGrid = this.Content as Grid; 
     //foreach (MenuItem mi in mainGrid.ContextMenu.Items) 
     // mi.IsChecked = mi == item; 

     // Load the selected skin. 
     this.ApplySkinFromMenuItem(item); 
    } 

    void ApplySkinFromMenuItem(MenuItem item) 
    { 
     // Get a relative path to the ResourceDictionary which contains the selected skin. 
     string skinDictPath = item.Tag as string; 
     Uri skinDictUri = new Uri(skinDictPath, UriKind.Relative); 

     // Tell the Application to load the skin resources. 
     App app = Application.Current as App; 
     app.ApplySkin(skinDictUri); 
    } 
} 
} 

回答

1

退房此鏈接:

ContextMenu in MVVM

您需要在上下文菜單項綁定到您的視圖模型的集合/屬性。 「這個。」將不起作用,因爲這是背後的代碼,並不會轉換爲視圖模型。

在視圖模型將這個:

class ContextItem : INotifyPropertyChanged 
{ 
    public string Name; 
    public ICommand Action; 
    public Brush Icon; 
} 

ObservableCollection<ContextItem> Items {get;set;} 

然後在視圖的上下文菜單:你想

<Grid.ContextMenu> 
    <ContextMenu ItemsSource="{Binding Items}/> 

什麼要「通」的觀點需要在屬性/收集您的視圖模型中,您將永遠不會直接在viewmodel中使用可視元素對象,如Gird/Context菜單。 WPF爲你處理綁定,這是WPF的主要優點。只要確保您爲屬性實施INotifyPropertyChanged。我沒有簡化樣本。

現在這並不意味着永遠不會有代碼背後的情況,但它應該只涉及視覺元素,而不是視覺元素綁定的數據。

希望這會有所幫助

+0

好吧,我明白你要去哪裏。你的建議指向一種完全的設計方式,所以我甚至不需要這個。作爲Grid的內容。我完美地理解你的意思,這很有道理。我以前做過這個,不知道爲什麼我沒有想到它。感謝您的正確方向。我沒有把這個問題作爲解決方案進行投票,但是我會盡可能地做到這一點。再次感謝! –

+0

很高興幫助。祝你好運。 –