2012-06-12 31 views
6

我正在學習WPF和MVVM(或者至少我正在嘗試......)。在Buttonclick上更改視圖

我創建了一個小樣本應用程序,它顯示一個帶有2個按鈕的窗口,每個應該顯示一個新的點擊視圖。所以我創建了3個UserControls(帶有2個按鈕的DecisonMaker,以及每個「clicktarget」的一個Usercontrol)。

所以我在MainWindowViewModel綁定的主窗口的CotentControl到一個名爲 「CurrentView」 屬性

代碼MainWindow.xaml的:

<Window x:Class="WpfTestApplication.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:WpfTestApplication" 
    Title="MainWindow" Height="350" Width="525"> 
<Window.DataContext> 
    <local:MainWindowViewModel /> 
</Window.DataContext> 
<Grid> 
    <ContentControl Content="{Binding CurrentView, Mode=OneWay}" /> 
</Grid> 
</Window> 

代碼MainWindowViewModel的:

class MainWindowViewModel 
{  
    private UserControl _currentView = new DecisionMaker(); 
    public UserControl CurrentView 
    { 
     get { return _currentView; } 
     set { _currentView = value; } 
    } 

    public ICommand MausCommand 
    { 
     get { return new RelayCommand(LoadMouseView); } 
    } 

    public ICommand TouchCommand 
    { 
     get { return new RelayCommand(LoadTouchView); } 
    } 

    private void LoadMouseView() 
    { 
     CurrentView = new UserControlMouse(); 
    } 

    private void LoadTouchView() 
    { 
     CurrentView = new UserControlTouch(); 
    } 
} 

最初的UserControl(DecisionMaker)按假設顯示。也調用方法LoadMouseView。但視圖不會改變。我錯過了什麼?

更新:非常感謝!我錯過了INotifyPropertyChanged接口。所有的答案都很棒,非常準確和有用!我不知道哪一個可以接受 - 我認爲這是接受「第一」答案的最公平的方法?

我接受了盲人回答,因爲它解決了問題,並幫助我更好地理解了MVVM。但是,每個人的回答都非常棒,謝謝大家!

+1

視圖模型應該沒有參照的圖/用戶控件。所以你應該從你的viewmodel中刪除它。這是一個很好的起點:http://msdn.microsoft.com/en-us/magazine/dd419663.aspx – blindmeis

回答

6

如果你想要做mvvm - 那麼你應該有沒有引用你view/usercontrols在你的viewmodel。你必須實施INotifyPropertyChanged! ps:如果你需要在你的Viewmodel中使用System.Windows命名空間 - 那麼有些地方是錯誤的。

你的情況

你需要什麼:

  • 1 mainviewmodel
  • 1視圖模型爲UserControlMouse
  • 1視圖模型爲UserControlTouch爲UserControlMouse
  • 1視圖/用戶控件
  • 1視圖/用戶控件的UserControlTouch

您的mainviewmodel應該至少有2個命令來切換CurrentView的視圖和1個屬性。在您的命令中,您只需將您的CurrentView設置爲正確的viewmodel實例。至少您需要爲每個視圖模型定義正確視圖的兩個數據模板。

public object CurrentView 
{ 
    get { return _currentView; } 
    set { 
     _currentView = value; this.RaiseNotifyPropertyChanged("CurrentView");} 
} 

XAML

<Window x:Class="WpfTestApplication.MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:local="clr-namespace:WpfTestApplication" 
Title="MainWindow" Height="350" Width="525"> 
<Window.Resources> 
<DataTemplate DataType="{x:Type local:MyMouseViewModel}"> 
    <local:MyMouseUserControlView/> 
    </DataTemplate> 
<DataTemplate DataType="{x:Type local:MyTouchViewModel}"> 
    <local:MyTouchUserControlView/> 
    </DataTemplate> 
</Window.Resources> 
<Window.DataContext> 
<local:MainWindowViewModel /> 
</Window.DataContext> 
<Grid> 

<!-- here your buttons with command binding, i'm too lazy to write this. --> 

<!-- you content control --> 
<ContentControl Content="{Binding CurrentView, Mode=OneWay}" /> 
</Grid> 
</Window> 
+0

這對我的學習進程有很多幫助和信息。我剛剛開始使用WPF/MVVM,因此我已經非常高興能夠獲得ICommand-Behavior的工作;) – basti

+0

如果您想要純MVVM,那麼該視圖不應該具有對視圖模型的引用。在應用程序啓動中實例化視圖和視圖模型,並將視圖模型綁定到啓動方法中的數據上下文。 –

+0

+1「如果你需要在你的Viewmodel中使用System.Windows命名空間 - 那麼有些地方是錯誤的。」我非常同意這一點;不幸的是,許多MVVM文章和框架建議從'ICommand'派生命令,這會在ViewModel中引入WPF程序集中不必要的依賴項。 – 2012-06-12 10:36:55

2

該視圖模型需要實現INotifyPropertyChanged。否則,當視圖模型中的屬性發生更改時,不會通知視圖。

class MainWindowViewModel : INotifyPropertyChanged 
{ 
    private UserControl _currentView = new DecisionMaker(); 

    public UserControl CurrentView 
    { 
     get { return _currentView; } 
     set 
     { 
      _currentView = value; 
      OnPropertyChanged("CurrentView"); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 
+0

謝謝我已經實現了這個(在你的答案後),它工作得很好。但是爲什麼我在這裏需要它而不是String類型的屬性? – basti

+1

這適用於你的情況,但它不是mvvm – blindmeis

1

您需要實現INotifyPropertyChangedMainWindowViewModel,這樣當CurrentView屬性更改視圖通知。

3

我會做這樣的事情來選擇你想要的輸入風格,到MainWindow我已經添加了一個屬性,讓我選擇輸入模式。

public enum UserInterfaceModes 
{ 
    Mouse, 
    Touch, 
} 

public UserInterfaceModes UserInterfaceMode 
{ 
    get { return (UserInterfaceModes)GetValue(UserInterfaceModeProperty); } 
    set { SetValue(UserInterfaceModeProperty, value); } 
} 

public static readonly DependencyProperty UserInterfaceModeProperty = DependencyProperty.Register("UserInterfaceMode", typeof(UserInterfaceModes), typeof(MainWindow), new UIPropertyMetadata(UserInterfaceModes.Mouse)); 

然後,對於xaml視圖部分,您可以使用觸發器選擇正確的模板。

<Style TargetType="{x:Type local:MainWindow}"> 
    <Style.Triggers> 
     <DataTrigger Binding="{Binding UserInterfaceMode}" Value="Mouse"> 
      <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="{x:Type local:MainWindow}"> 
          <Grid Background="Red"/> 
         </ControlTemplate> 
        </Setter.Value> 
      </Setter> 
     </DataTrigger> 
     <DataTrigger Binding="{Binding UserInterfaceMode}" Value="Touch"> 
      <Setter Property="Template"> 
        <Setter.Value> 
         <ControlTemplate TargetType="{x:Type local:MainWindow}"> 
          <Grid Background="Blue"/> 
         </ControlTemplate> 
        </Setter.Value> 
       </Setter> 
     </DataTrigger> 
    </Style.Triggers> 
</Style> 
+0

絕對是一個很好的答案,這將幫助我在未來!對於我現在面臨的測試/學習的特定問題,它不是100%適用的,因爲它會否定我的整個按鈕佈局;) 但是在未來的「真實」實現中,我肯定會回來查看您的代碼! – basti

+0

Aww,你會讓我臉紅! :D – Andy

1

這聽起來像你想要的行爲是相當多的,你有[TabControl][1]得到什麼 - 爲什麼不利用這個內置的控制,只是結合兩個標籤的DataContext到同一視圖模型。

這也有一個好處,你的視圖模型不會知道視圖類(我假設UserControlMouse等是用戶控件)。

注意:如果您需要視圖模型知道它是處於觸摸還是鼠標模式,這將不適用。

+0

謝謝。這是這裏的總體目標,用鼠標和TouchMode來劃分UserControl。所以一旦在開始時,我將需要「知道」啓動用戶控件。 – basti

+0

@chiffre如果你想做MVVM,你仍然不應該在視圖模型中引用視圖組件(用戶控件) –

+0

我想我會遵循blindmeis的答案來做未來的工作。感謝您在這種情況下的巨大幫助! – basti