2012-02-17 47 views
2

我試圖設置一個MVVM風格的應用程序,我想我會讓自己陷入與這些項目的交互的幾個節點,並希望有人可以提供幫助。我在這裏做錯了什麼嗎?如何最好地應用WPF MVVM?

我想我的主要2個問題是

  1. 我應該如何從我的模型去我的看法。目前我正在嘗試通過轉換器完成此操作。
  2. 如果使用轉換器是正確的,我該如何正確工作?我相信Node構造函數中設置的datacontext被包含轉換器的XAML所取代。

我的類(簡化比特):

IFieldDescription

​​

節點模型

// Class that is supposed to be the ViewModel 
public class NodeModel : NotifyPropertyChanged 
{ 
    internal NodeModel() { } 

    public NodeModel(IFieldDescription fieldDescription) 
    {    
     this.FieldDescription = fieldDescription; 
    } 

    protected IFieldDescription FieldDescription 
    { 
     get { return this.fieldDescription; } 
     set { 
     this.fieldDescription = value; 
     this.OnPropertyChanged("Name"); 
     this.OnPropertyChanged("Disabled"); 
     this.OnPropertyChanged("PrimaryKey");  } 
    } 
    private IFieldDescription fieldDescription; 

    public String Name { get { return this.FieldDescription.Name; } } 
    public Boolean Disabled { get { return this.FieldDescription.Disabled; } } 
} 

節點 後面

public Node(NodeModel model) 
{ 
    this.DataContext = model; 
    this.InitializeComponent(); 
} 

代碼XAML

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:local="clr-namespace:GO" x:Class="GO.Node" Background="White" 
    > 

    <Grid x:Name="LayoutRoot"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="Auto"/> <!-- Primary Key Icon --> 
      <ColumnDefinition Width="Auto"/> <!-- Type Icon --> 
      <ColumnDefinition/>     <!-- Node Text --> 
      <ColumnDefinition Width="Auto"/> <!-- Option Cog --> 
      <ColumnDefinition Width="Auto"/> <!-- Match Icon --> 
      <ColumnDefinition Width="Auto"/>  
     </Grid.ColumnDefinitions> 
     <Grid.Resources> 
      <local:AttributeDataTypeConverter x:Key="DateTypeConverter"/> 
     </Grid.Resources> 
     <Image Grid.Column="0" Source="C:\Users\ian.wright\Documents\Expression\Blend 4\Projects\GO\GO\Resources\Images\PrimaryKey.png" Stretch="None" Visibility="{Binding Path=IsPrimaryKey}"/> 
     <Image Grid.Column="1" Source="{Binding Path=Type, Converter={StaticResource DateTypeConverter}}" Stretch="None"/> 
     <TextBlock Grid.Column="2" Text="{Binding Path=Name}" VerticalAlignment="Bottom" Margin="0,0,0,2"/> 
     <Image Grid.Column="3" Source="C:\Users\ian.wright\Documents\Expression\Blend 4\Projects\GO\GO\Resources\Images\Cog.png" Stretch="None" Visibility="{Binding Path=HasOptions}"/> 
     <Image Grid.Column="4" Source="{Binding Path=CastType}" Stretch="None"/> 
    </Grid> 
</UserControl> 

主窗口

<Window 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" 
x:Class="GO.MainWindow" 
xmlns:local="clr-namespace:GO" 
x:Name="Window" 
Title="MainWindow" 
Width="640" Height="480"> 

<Grid Width="200" Height="500"> 
    <Grid.Resources> 
     <local:NodeConverter x:Key="NodeConverter"/> 
     <local:ModelToViewConverter x:Key="ModelConverter"/> 
    </Grid.Resources> 
     <!--<ListView Grid.Column="1" ItemsSource="{Binding Path=FieldDescriptions, Converter={StaticResource ModelConverter}}">--> 

    <ListView Grid.Column="1" ItemsSource="{Binding Path=FieldDescriptions}"> 
     <ListView.ItemTemplate> 
      <DataTemplate> 
       <local:Node DataContext="{Binding Converter={StaticResource ModelConverter}}" /> 
      </DataTemplate> 
     </ListView.ItemTemplate> 
     </ListView> 
</Grid> 

的主窗口中的數據上下文被設置到一個新的RD(),其被定義如下: RD

public class RD 
    { 
     private IEnumerable<IFieldDescription> GetTestData() 
     { 
      yield return new FieldDescription("String", true); 
      yield return new FieldDescription("Integer", false); 
      yield return new FieldDescription("Double", false); 
      yield return new FieldDescription("Date", false); 
      yield return new FieldDescription("Enum", false); 
     } 

     public virtual ObservableCollection<IFieldDescription> FieldDescriptions 
     { 
      get { return new ObservableCollection<IFieldDescription>(GetTestData()); } 
     } 
    } 

我的任何轉換器目前被定義爲:

public class ModelToViewConverter : IValueConverter 
    { 
     public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      if (value == null) 
       return null; 

      if (value is IFieldDescription) 
      { 
       NodeModel model = new NodeModel((IFieldDescription)value); 
       return new Node(model); 
      } 

      return null; 
     } 

     public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 
     { 
      throw new NotImplementedException(); 
     } 
    } 
+2

請,永遠不要設置的一些例子'this.DataContext'在Control的構造函數中使用。用這種方法你會用自己的腿打自己。用於控制的'DataContext'應該只從外部設置,通常從其父級設置。有一天在構造函數中設置了'this.DataContext = ...',你會遇到一個噩夢,試圖理解「爲什麼我的綁定不起作用在這個控件上」 – Snowbear 2012-02-17 16:07:31

+0

@Snowbear:感謝評論,建議指出。我仍然試圖讓我的頭腦圍繞WPF的基礎知識,所以很高興承認我可能犯了一些錯誤。 – Ian 2012-02-17 16:29:32

回答

4

我通常使用DataTemplates爲與ModelViewModel

唯一的地方,我查看的代碼隱藏以往追平View當它將啓動ViewModel設置爲啓動視圖的DataContext時,啓動引用Model或ViewModel。其他的一切都與DataTemplates(或Silverlight的DataTemplateSelectors)聯繫在一起。

(實際上是有時公平我需要做一些特別的東西,我會投對象的DataContext作爲視圖模型或模型的代碼隱藏,但這些情況是罕見的,我認爲他們爲黑客)

例如,設置啓動視圖/視圖模型:

public partial class App : Application 
{ 
    protected override void OnStartup(StartupEventArgs e) 
    { 
     base.OnStartup(e); 

     var app = new ShellView(); 
     var context = new ShellViewModel(); 
     app.DataContext = context; 
     app.Show(); 
    } 
} 

這裏的一些的DataTemplates的例子:

<Window.Resources> 
    <DataTemplate DataType="{x:Type local:SomeViewModel}"> 
     <local:SomeViewForViewModel /> 
    </DataTemplate> 

    <DataTemplate DataType="{x:Type local:SomeModel}"> 
     <local:SomeViewForModel /> 
    </DataTemplate> 
</Window.Resources> 

最後,我將使用ContentControls在我的XAML,我想顯示自己的看法

<ContentControl Content="{Binding SomeViewModelProperty}" /> 

<ContentControl Content="{Binding SomeModelProperty}" /> 

有時甚至沒有必要ContentControls。例如,如果您將ListView綁定到ObservableCollection<NodeModel>,則ListView中的每個項目都將是NodeModel類型的對象,WPF將自動爲其提供DataTemplate。

<ListView ItemsSource="{Binding Path=CollectionOfNodeModel}"> 
    <ListView.Resources> <!-- Could also put this in Window.Resources --> 
     <DataTemplate DataType="{x:Type local:NodeModel}"> 
      <local:Node /> <!-- DataContext will implicitly be the NodeModel object --> 
     </DataTemplate> 
    </ListView.Resources> 
</ListView> 

MVVM背後的想法是,在您的ViewModels整個應用程序的功能,並且視角只是它坐落在的ViewModels的頂部,使他們一個漂亮的UI更加人性化。在一個完美的世界,視圖可以很容易地通過任何其他UI更換

如果你有興趣,我有我的博客simple MVVM example包含使用MVVM設計模式