I am using the MVVM pattern and came across this problem.
I would like to create a SpecificProductViewModel object by passing the product for which I want the specific view by parameter from my ProductListViewModel class (which contains all the products) with the following function:
private void OnProductNav(tblMATProduct product)
{
if (product != null)
{
CurrentProduct = product;
SpecificProductVM = new SpecificProductViewModel(product);
CurrentProductViewModel = SpecificProductVM;
SpecificProductVM.Product = product;
}
}
I would then need to assign the DataContext of my SpecificProductView to my SpecificProductViewModel in the code-behind like this:
DataContext = new SpecificProductViewModel();
The problem however, is that a new object of SpecificProductViewModel gets created by doing so and the DataContext is therefore not using the data (tblMATProduct product) passed by parameter when the object was created from the ProductListViewModel class.
Would there be a way to assign the DataContext directly from the ProductListViewModel class or to be able to get the tblMATProduct product from the code-behind or in the xaml?
Thanks in advance!
Edit 1: I was thinking like Lennart by doing this:
DataContext = new ProductListViewModel();
The problem though is that the specificViewModel instance needs to be created right after the user clicks on a specific product.
Therefore, if we create a new instance of ProductListViewModel in the code-behind and assign it ProductListViewModel, the instance won't know on what the user clicked and won't call the function OnProductNav.
Edit 2 : Adding more precision.
class ProductListViewModel : ViewModelBase
{
private SpecificProductViewModel SpecificProductVM;
public tblMATProduct CurrentProduct { set; get; }
public ViewModelBase CurrentProductViewModel
{
get { return _currentProductViewModel; }
set { SetProperty(ref _currentProductViewModel, value); }
}
public ProductListViewModel() {
ProductNavCommand = new MyICommand<tblMATProduct>(OnProductNav);
}
private void OnProductNav(tblMATProduct product)
{
if (product != null)
{
SpecificProductVM = new SpecificProductViewModel(product);
CurrentProductViewModel = SpecificProductVM;
}
}
}
In my ProductListView, I have a DataGrid with all my products where the user can select a row (specific product) by clicking on it.
<Datagrid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ProductNavCommand}"
CommandParameter="{Binding Path=SelectedItem, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
Here is my SpecificProductViewModel
class SpecificProductViewModel : ViewModelBase
{
private tblMATProduct _product;
public tblMATProduct Product
{
get { return _product; }
set { SetProperty(ref _product, value); }
}
public SpecificProductViewModel()
{
}
public SpecificProductViewModel(tblMATProduct product)
{
Product = product;
}
}
And here is a few lines of code from SpecificProductView.xaml which will bind to SpecificProductViewModel.cs
<Grid Background="Red" Height="800" Width="1350" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding Product.FormatID}" FontSize="32" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBlock Text="{Binding Product.SpecificProductVM, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" FontSize="32" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
<TextBlock Text="{Binding Product.GradeID}" FontSize="32" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Top"/>
</Grid>
Edit 3: Adding another layer of precision
To answer mm8's question, as I have just started working with WPF and MVVM, I have always assumed that if the View's DataContext
is binded to the corresponding ViewModel
, the instance of the View
will be created automatically when a ViewModel
instance is created via the xaml Datatemplate in ProductListView.xaml
:
<DataTemplate DataType = "{x:Type viewModels:SpecificProductViewModel}">
<products:SpecificProductView/>
</DataTemplate>
And this is where I would usually create my DataContext
.
SpecificProductView.xaml.cs
public partial class SpecificProductView : UserControl
{
public SpecificProductView()
{
InitializeComponent();
DataContext = new SpecificProductViewModel();
}
}
Edit 4: Explaining how the Data Template is handled
Here is the ContentControl which launches the view of the SpecificProductView. It uses
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Column="0" Grid.Row="0">
<ContentControl Content="{Binding CurrentProductViewModel}"/>
</Grid>
It binds to this which determines which product should be displayed in the SpecificProductView
.
public ViewModelBase CurrentProductViewModel
{
get { return _currentProductViewModel; }
set { SetProperty(ref _currentProductViewModel, value); }
}
Since you are using a ContentControl
that binds to the CurrentProductViewModel
property that you set in your view model, you should simply remove the following part that sets the DataContext
of the UserControl
to an new instance of the view model from your XAML:
<UserControl.DataContext>
<viewModel:SpecificProductViewModel/>
</UserControl.DataContext>
The UserControl
will then inherit the view model returned by the CurrentProductViewModel
as its DataContext
.
It is generally speaking a bad idea to explicitly set the DataContext
property of a UserControl
, either in XAML as well as in the code-behind, as this breaks the inheritance of the DataContext
from the parent element.