确保每个子UserControl都创建自己的ViewModel实例



我为一个客户处理一个项目,并决定利用WPF在数据绑定和UI设计的声明性方法方面的优势。但我在理解Views和ViewModels之间的关系时遇到了一个巨大的问题。

我有一个UserControl(ParentUserControl)和一个子UserControl(ChildUserControl)。在这个ParentUserControl中,我有一个ContentPresenter,它可以容纳ChildUserControl的多个实例。ChildUserControl有多个组合框和文本框,显示我的模型中的信息。用户可以通过单击"添加新"按钮,在ParentUserControl中打开任意数量的ChildUserControl。在我的ParentViewModel中,我存储了每个创建的ChildViewModel的实例,该用户向ParentUserControl添加了一个新的ChildUserControl。用户可以通过"查看下一个"one_answers"查看上一个"按钮浏览ChildUserControls。

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

我的问题是,我如何才能绝对肯定每次实例化新视图时,它都会获得自己的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

我看到了几个问题:

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

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

如何在子视图之间导航?我看不到相关代码。

最新更新