WPF - 如何确保每个视图都有自己的ViewModel实例? [英] WPF - How do I ensure each view gets its own ViewModel instance?

查看:109
本文介绍了WPF - 如何确保每个视图都有自己的ViewModel实例?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对WPF和MVVM模式都很陌生,所以请光临我。我为客户开发项目,并决定在数据绑定和UI设计的声明方法方面利用WPF的优势。但是我在理解我的Views和ViewModel之间的关系时遇到了很大的问题。



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



所有这些都很有效,除非用户进行选择或更改任何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

高度 = 350 宽度 = 525 >
< UserControl.Resources >
< DataTemplate DataType = {x:Type local:ChildUserControlViewModel} >
< local:ChildUserControl / >
< / DataTemplate >
< /UserControl.Resources >

< UserControl。 DataContext >
< local:MainViewModel / >
< / UserControl.DataContext >
< < span class =code-leadattribute>网格 >
< StackPanel 宽度 = auto 高度 = 200 >
< ContentPresenter 内容 = {Binding CurrentView} Horizo​​ntalAlignment = 拉伸 VerticalAlignment = 拉伸 < span class =code-attribute> >
< < span class =code-leadattribute> / ContentPresenter >
< / StackPanel >
< StackPanel >
< 按钮 命令 = {Binding ChangeUserControlCommand} 内容 = 点击更改视图 / >
< / StackPanel >


< / Grid >
< / UserControl >





ChildUserControl:



 <   UserControl     x:Class   =  ChildUserControl  
xmlns = http://schemas.microsoft.com/winfx/2006/xaml/presentation
< span class =code-attribute> xmlns:x = http://schemas.microsoft.com/winfx/2006/xaml
xmlns:mc = http ://schemas.openxmlformats.org/markup-compatibility/2006
< span class =code-attribute>
xmlns:d = http://schemas.microsoft.com/expression/blend/2008
xmlns:local = clr-namespace :MVVM_Light_Test_Application
mc:可忽略 = d
d:DesignHeight = 300 d:DesignWidth = 300 已移除 = {Binding BGColor,UpdateSourceTrigger = PropertyChanged} >
< UserControl.DataContext >
< span class =code-keyword>< local:ChildUserControlViewModel / >
< / UserControl.DataContext >
< 网格 >
< Grid.ColumnDefinitions >
< ColumnDefinition 宽度 = 149 * / >
< ColumnDefinition 宽度 = 151 * / >
< < span class =code-leadattribute> / Grid.ColumnDefinitions >

< StackPanel 高度 = 200 宽度 = auto >
< StackPanel >
< ComboBox x:名称 = cboExampleObjects 保证金 = 5 ItemsSource = {绑定客户} DisplayMemberPath = 详细信息 SelectedItem = {Binding SelectedCustomer} / >
< / StackPanel >
< StackPanel Grid.Column = 1 >
< TextBox x:名称 = txtObjectPropertyValue 文字 = {Binding SelectedCustomer.Name} 保证金 = 5 / >
< / StackPanel >
< / StackPanel >
< / Grid >
< span class =code-keyword>< / UserControl >





父母视图型号:



 公共  MainViewModel 
继承 ViewModelBase

公共 Sub ()
_currentView = ChildUserControlViewModel
ChangeUserControlCommand = RelayCommand( AddressOf ChangeUserControl)
结束 Sub

私有 _currentView 作为 ViewModelBase
公共 属性 CurrentView 作为 ViewModelBase
获取
返回 _currentView
结束 获取
设置(值 As ViewModelBase)
_currentView = value
RaisePropertyChanged( CurrentView
结束 设置
结束 属性

公共 属性 ChangeUserControlCommand As RelayCommand


公共 Sub ChangeUserControl()
CurrentView = ChildUserControlViewModel
CType (CurrentView,ChildUserControlViewModel).BGColor = Nothing
End Sub
结束





儿童景观型号:



  Imports  System.Collections.ObjectModel 

Public Class ChildUserControlViewModel
继承 ViewModelBase

公共 Sub New ()
_customes = ObservableCollection( 客户)(列表( 客户)({客户使用 {.Name = TestName1,。CustomerProumber = 1 },客户使用 {。Name = TestN ame2,。CustomerProumber = 2 }}))
结束 Sub

私有 _customers As ObservableCollection( 客户)
公共 属性客户作为 ObservableCollection( 客户)
获取
返回 _customers
结束 获取
设置(值 As ObservableCollection ( 客户))
_customers = value
RaisePropertyChanged( Customers
End 设置
结束 属性

私人 _selectedCustomer 作为客户
公开 属性 SelectedCustomer 作为客户
获取
返回 _selectedCustomer
结束 获取
设置(值 As Customer)
如果 _selectedCustomer 没什么 OrElse String .Compare(value.Name,_selectedCustomer.Name)<> 0 然后
_selectedCustomer = value
RaisePropertyChanged( SelectedCustomer
结束 如果
结束 设置
结束 属性

私有 _bgColor 作为 SolidColorBrush
公共 属性 BGColor As SolidColorBrush
获取
Dim random As 随机
返回 SolidColorBrush(Color.FromArgb(< span class =code-digit> 50 ,Random。 Next 0 ,< span class =code-digit> 255 ),随机。下一步 0 255 ),随机。下一步 0 255 )))
结束 获取
设置(值 As SolidColorBrush)
Dim random As 随机
_bgColor = SolidColorBrush(颜色。 FromArgb( 50 ,随机。下一步 0 255 ),随机。下一步 0 255 ),随机。下一步 0 255 )))
RaisePropertyChanged( BGColor
结束 设置
结束 属性
结束

解决方案

SO上的优秀绅士为我回答了我的问题。在绑定到ContentPresenter时,我没有意识到子视图为它们设置了数据上下文。一旦我删除了< usercontrol.datacontext>来自ChildUserControl的标签,一切都像梦一样。要查看我收到的答案,请单击 此处 [ ^ ]

I am quite new to WPF and the MVVM pattern so please bare with me. I working on a project for a client and decided to utilize the benefits of WPF in terms of data binding and the declarative approach to UI design. But I'm having a huge issue understanding the relationship between my Views and ViewModels.

I have a UserControl (ParentUserControl), and a child UserControl (ChildUserControl). Within this ParentUserControl I have a ContentPresenter that can hold multiple instances of ChildUserControl. The ChildUserControl has multiple comboboxes and textboxes displaying information from my Model. The user can open as many ChildUserControls within the ParentUserControl as they wish by clicking an 'Add New' Button. In my ParentViewModel, I'm storing the instances of each ChildViewModel that is created with the user adds a new ChildUserControl to the ParentUserControl. The user can navigate through the ChildUserControls through 'View Next' and 'View Previous' Buttons.

All of this works great, except if a user makes a selection or changes the text of any control in any ChildUserControl, the change propagates throughout all the ChildUserControls the user has created / sharing one ViewModel for all views. I've tried everything that I can think of, with and without using the MVVM Light tool kit (which seems like it's going to save me a ton of time in the future).

*My question is how can I be absolutely positively sure that each time a new View is instantiated that it gets its own instance of it's ViewModel? I've been stuck on this for days!!! Thanks!*

'NOTE: This code is not of an actual product. It's to simply demonstrate the issue I'm having with a real application. Don't be afraid to answer in C# if need be, my preferred language anyway. Oh, and I'm trying to complete this project with Pure MVVM in mind. Thank you!!!

Code:

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" removed="{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>



Parent View Model:

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



Child View Model:

Imports 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

解决方案

A fine gentleman on SO answered my question for me. I did not realize that the child views had their data context set for them when binding to the the ContentPresenter. Once I removed the <usercontrol.datacontext> tag from the ChildUserControl, everything worked like a dream. To view the answer I received click Here [^]


这篇关于WPF - 如何确保每个视图都有自己的ViewModel实例?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆