2014-02-17 121 views
3

对于WPF和MVVM模式,我很新,所以请随身携带。我为客户开发一个项目,并决定利用WPF在数据绑定和声明式UI设计方面的优势。但我有一个很大的问题,理解我的Views和ViewModels之间的关系。确保每个子UserControl创建自己的ViewModel实例

我有一个UserControl(ParentUserControl)和一个子UserControl(ChildUserControl)。在这个ParentUserControl中,我有一个ContentPresenter可以容纳多个ChildUserControl实例。 ChildUserControl有多个组合框和文本框,显示来自我的模型的信息。用户可以通过单击“添加新的”按钮,在ParentUserControl中打开任意多个ChildUserControls。在我的ParentViewModel中,我存储用户创建的每个ChildViewModel的实例将新的ChildUserControl添加到ParentUserControl。用户可以通过'查看下一个'和'查看以前的'按钮来浏览ChildUserControls。

所有这些工作都很好,除非用户在任何ChildUserControl中进行选择或更改任何控件的文本,否则该更改会传播到用户为所有视图创建/共享一个ViewModel的所有ChildUserControl。我尝试了所有我能想到的,无论是否使用MVVM Light工具包(这似乎将在未来为我节省大量时间)。

我的问题是我怎么能绝对肯定每次一个新的View被实例化,它获得它自己的ViewModel的实例?我已经坚持了几天!谢谢!

'注意:此代码不是实际产品。这只是为了展示我与真正的应用程序有关的问题。无论如何,如果需要的话,不要害怕在C#中回答我的首选语言。哦,我试图用Pure MVVM来完成这个项目。谢谢!!!

代码:

ParentUserControl:

<UserControl x:Class="ParentUserControl" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:local="clr-namespace:MVVM_Light_Test_Application" 
    Height="350" Width="525"> 
    <UserControl.Resources> 
     <DataTemplate DataType="{x:Type local:ChildUserControlViewModel}"> 
      <local:ChildUserControl/> 
     </DataTemplate> 
    </UserControl.Resources> 

    <UserControl.DataContext> 
     <local:MainViewModel/> 
    </UserControl.DataContext> 
    <Grid> 
     <StackPanel Width="auto" Height="200"> 
      <ContentPresenter Content="{Binding CurrentView}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" > 
      </ContentPresenter>    
     </StackPanel> 
     <StackPanel> 
      <Button Command="{Binding ChangeUserControlCommand}" Content="Click To Change View"/> 
     </StackPanel> 
    </Grid> 
</UserControl> 

ChildUserControl:

<UserControl x:Class="ChildUserControl" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      xmlns:local="clr-namespace:MVVM_Light_Test_Application" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300" Background="{Binding BGColor, UpdateSourceTrigger=PropertyChanged}"> 
    <UserControl.DataContext> 
     <local:ChildUserControlViewModel/> 
    </UserControl.DataContext> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="149*"/> 
      <ColumnDefinition Width="151*"/> 
     </Grid.ColumnDefinitions> 

     <StackPanel Height="200" Width="auto"> 
      <StackPanel> 
       <ComboBox x:Name="cboExampleObjects" Margin="5" ItemsSource="{Binding Customers}" DisplayMemberPath="Details" SelectedItem="{Binding SelectedCustomer}"/> 
      </StackPanel> 
      <StackPanel Grid.Column="1"> 
       <TextBox x:Name="txtObjectPropertyValue" Text="{Binding SelectedCustomer.Name}" Margin="5"/> 
      </StackPanel> 
     </StackPanel> 
    </Grid> 
</UserControl> 

父视图模型:

Public Class MainViewModel 
    Inherits ViewModelBase 

    Public Sub New() 
     _currentView = New ChildUserControlViewModel 
     ChangeUserControlCommand = New RelayCommand(AddressOf ChangeUserControl) 
    End Sub 

    Private _currentView As ViewModelBase 
    Public Property CurrentView As ViewModelBase 
     Get 
      Return _currentView 
     End Get 
     Set(value As ViewModelBase) 
      _currentView = value 
      RaisePropertyChanged("CurrentView") 
     End Set 
    End Property 

    Public Property ChangeUserControlCommand As RelayCommand 


    Public Sub ChangeUserControl() 
     CurrentView = New ChildUserControlViewModel 
     CType(CurrentView, ChildUserControlViewModel).BGColor = Nothing 
    End Sub 
End Class 

子视图模式:

进口System.Collections.ObjectModel

Public Class ChildUserControlViewModel 
    Inherits ViewModelBase 

    Public Sub New() 
     _customes = New ObservableCollection(Of Customer)(New List(Of Customer)({New Customer With {.Name = "TestName1", .CustomerNumber = 1}, New Customer With {.Name = "TestName2", .CustomerNumber = 2}})) 
    End Sub 

    Private _customers As ObservableCollection(Of Customer) 
    Public Property Customers As ObservableCollection(Of Customer) 
     Get 
      Return _customers 
     End Get 
     Set(value As ObservableCollection(Of Customer)) 
      _customers = value 
      RaisePropertyChanged("Customers") 
     End Set 
    End Property 

    Private _selectedCustomer As Customer 
    Public Property SelectedCustomer As Customer 
     Get 
      Return _selectedCustomer 
     End Get 
     Set(value As Customer) 
      If _selectedCustomer Is Nothing OrElse String.Compare(value.Name, _selectedCustomer.Name) <> 0 Then 
       _selectedCustomer = value 
       RaisePropertyChanged("SelectedCustomer") 
      End If 
     End Set 
    End Property 

    Private _bgColor As SolidColorBrush 
    Public Property BGColor As SolidColorBrush 
     Get 
      Dim random As New Random 
      Return New SolidColorBrush(Color.FromArgb(50, Random.Next(0, 255), Random.Next(0, 255), Random.Next(0, 255))) 
     End Get 
     Set(value As SolidColorBrush) 
      Dim random As New Random 
      _bgColor = New SolidColorBrush(Color.FromArgb(50, random.Next(0, 255), random.Next(0, 255), random.Next(0, 255))) 
      RaisePropertyChanged("BGColor") 
     End Set 
    End Property 
End Class 

回答

2

我看到几个问题:

  1. 在XAML中设置ChildUserControl.DataContext,但ChildUserControl实例已经从ContentPresenter结合获取数据的上下文。你可以安全地删除它。

  2. ContentPresenter只能包含一个子控件。当您创建一个新的视图模型并将其设置为CurrentView时,旧的视图模型将被遗忘,旧的ChildUserControl将被删除,然后将创建一个代表新视图模型的新的ChildUserControl

如何在子视图间导航?我没有看到相关的代码。

+0

删除XAML中的'ChildUserControl.DataContext'确实有窍门!你是上帝!我没有意识到,绑定到“ContentPresenter”时,子视图的数据上下文已设置。我真的不能够感谢你! –

+0

如果一个控件包含另一个控件,则子控件将(几乎)始终从父控件继承数据上下文,无论它如何添加(演示者,模板,显式等)。如果父控件是一个列表('ItemsControl'),则子控件将绑定到来自'ItemsSource'的单个项目。 – Athari

+0

这实际上使事情变得更容易。另外,我在我的子用户控件的DataContext上收到了设计时错误。你刚刚救了我一次与我的客户谈话,我害怕哈哈。再次感谢! –

相关问题