I am new to WPF and I already found some posts pointing in the right direction but I seem to miss something.
I have an application with a main window that consists of tabpages. Each tab page contains a tree of objects and next to it the selected object should be displayed.
I use the MVVM pattern (as far as I understand it), following Demo 2
I have tried this and that solution.
Currently the XAML for my ProductTab looks like this:
<UserControl x:Class="ProductPrototype.View.ProductTab"
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:views="clr-namespace:ProductPrototype.View"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200px" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView ItemsSource="{Binding Products}" Grid.Column="0">
<TreeView.ItemContainerStyle>
<!-- This Style binds a TreeViewItem to a PersonViewModel. -->
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="FontWeight" Value="Normal" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name, StringFormat= '\{0\} (Product)'}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<!--<views:ProductDescription Grid.Column="1"/>-->
<ListBox Grid.Column="1" ItemsSource="{Binding SelectedProduct}">
<TextBox Text="{Binding Name}" Width="100px"/>
</ListBox>
</Grid>
The cs file just assigns a ProductTreeViewModel to the DataContext.
My ProductTreeviewModel looks like this (I had to add a setter to SelectedProduct, because otherwise I can't bind to it, the solution from my third link uses it without set):
namespace ProductPrototype.ViewModel
{
public class ProductTreeViewModel : TreeViewItemViewModel
{
public ProductViewModel SelectedProduct
{
get {
//the first time this is called productViewModel is null
ProductViewModel productViewModel = Products.FirstOrDefault(i => i.IsSelected);
//MessageBox.Show(productViewModel.Name);
return productViewModel;
}
set { MessageBox.Show("Setter is called"); }
}
private ReadOnlyCollection<ProductViewModel> _products = null;
public ReadOnlyCollection<ProductViewModel> Products
{
get { return _products; }
}
public ProductTreeViewModel(Product[] products) : base (null, true)
{
_products = new ReadOnlyCollection<ProductViewModel>(
(from product in products
select new ProductViewModel(product))
.ToList());
}
}
}
There are several things that I am not sure about:
- Does the View know that the SelectedProduct changed, if not, how do I inform it? (As I understand it the ViewModel should not know about the View which makes absolutely sense)
- Is the binding correct? I managed to bind the Tree correctly so I don't see why the SelectedProduct part should be wrong.
- Do I have to implement some Event? How? I have tried to fire a PropertyChanged event but I am not sure how the view should notice it.
- I have read about attached behaviours in that context, would that be the way to go? If so, are there any tutorials that are easy to grasp?
- Is there any book about my issue (TreeView and DataBinding), because I've looked into several tutorials (10+) and 2 books but I haven't found any helpful information.