对于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
删除XAML中的'ChildUserControl.DataContext'确实有窍门!你是上帝!我没有意识到,绑定到“ContentPresenter”时,子视图的数据上下文已设置。我真的不能够感谢你! –
如果一个控件包含另一个控件,则子控件将(几乎)始终从父控件继承数据上下文,无论它如何添加(演示者,模板,显式等)。如果父控件是一个列表('ItemsControl'),则子控件将绑定到来自'ItemsSource'的单个项目。 – Athari
这实际上使事情变得更容易。另外,我在我的子用户控件的DataContext上收到了设计时错误。你刚刚救了我一次与我的客户谈话,我害怕哈哈。再次感谢! –