0

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.
Community
  • 1
  • 1
Verena Haunschmid
  • 1,252
  • 15
  • 40
  • Where do you want the SelectedItem Binding ? On the ListBox or the TreeView ? and what do you mean by change the view ? Navigate to another usercontrol ? And your ListBox.Itemssource alwys contains only 1 item or ? – Mark Jul 29 '14 at 08:07
  • The TreeView works already. I want to display the properties of the selected item in the ListBox. And if I select something else I want to display that instead. – Verena Haunschmid Jul 29 '14 at 08:08
  • 1
    1. You should implement `INotifyPropertyChanged` on the `ProductTreeViewModel` and raise the `OnPropertyChanged` when `SelectedProduct` changes, that's how the View is informed when it changes. 2. If you dont have to have a public setter, make the binding `Mode=OneWay` 3. WPF automatically wires up to the OnPropertyChanged in the view. Is the `ProductViewModel` a collection? If not you shouldnt be using a ListBox to display it Hopefully this should point you in the right direction – Martin Grundy Jul 29 '14 at 08:09
  • ok thanks, I did 1 and 2. But still it doesn't do anything. Is it possible that I have to somehow link the View to the ViewModel? Or is the DataContext enough? ad 3: no it is not a Collection, what else should I use? I want to display several properties like name, prize and so on as a list. – Verena Haunschmid Jul 29 '14 at 08:29
  • ah, maybe it is a problem that I implemented it in a base class of ProductTreeViewModel, is that possible? Is is implemented as `protected virtual void OnPropertyChanged(string propertyName)` in the class TreeViewItemViewModel – Verena Haunschmid Jul 29 '14 at 08:33
  • 1
    Yes that's fine to do it in the base class, just remember to raise it when you change the property, it is not raised automatically. If you want to show a list of properties, you could use any container object, Grid, StackPanel etc and add a TextBlock or something for each item you want to display. I dont think the IsSelected property of the ProductViewModel is being set, I cant see any binding to set that value or do you do that in code? – Martin Grundy Jul 29 '14 at 08:39
  • Ok, I checked, it's raised correctly. It wasn't displayed yet because of the ListBox, now I use StackPanel with TextBoxes. Great! – Verena Haunschmid Jul 29 '14 at 09:49

0 Answers0