對於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上收到了設計時錯誤。你剛剛救了我一次與我的客戶談話,我害怕哈哈。再次感謝! –