Click here to Skip to main content
15,889,992 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
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:

XML
<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:

XML
<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:

VB
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:

VB
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
Posted

1 solution

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 [^]
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900