views:

543

answers:

1

I just noticed some strange behaviour of WPF's TreeView. I added both ItemContainerStyle to bind to "IsSelected" of my ViewModel and an ItemsTemplated for custom display of my data. But now the user cannot change the selected node anymore. For testing purposes I created a similar UI using ListView and Expander. This version works as excepted. Any tips why TreeView does fail?

 <TreeView ItemsSource="{Binding ElementName=frame, Path=list}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}" >
                <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
            </Style>
        </TreeView.ItemContainerStyle>
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate>
                <TreeViewItem Header="{Binding}">
                    <TextBlock Text="{Binding Path= Item.SomeData}"/>
                </TreeViewItem>
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>

EDIT: My data are not hierachical. I just want to get the "collapse" feature on displaying a list. Item.SomeData is not a list. Display of data is as desired. Only selection by mouse fails!

alt text

+1  A: 

TreeViews work differently. The Items inside a HierarchicalDataTemplate are TreeViewItems and any control you specify inside the HierarchicalDataTemplate will function as its Header. So, basically you are specifying that the Items in your TreeView are TreeViewItems with TreeViewItems as their headers! Instead try this:

 <HierarchicalDataTemplate ItemsSource="{Binding Items}"> 
      <Label Content="{Binding}"/>
      <HierarchicalDataTemplate.ItemTemplate>
         <DataTemplate> 
             <TextBlock Text="{Binding Path= Item.SomeData}"/> 
         </DataTemplate>
      </HierarchicalDataTemplate.ItemTemplate> 
  </HierarchicalDataTemplate>

EDIT: I could not reproduce a DataSource that produces the properties you want to bind to, so I wrote some simple code of my own that shows how it all works. Hopefully you will be able to adapt it to your needs:

<TreeView ItemsSource="{Binding}" Name="Tree">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}" >
                    <Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}"/>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Items}">
                    <Label Content="{Binding Name}"/>
                    <HierarchicalDataTemplate.ItemTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding Path= SomeData}"/>
                        </DataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>

using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;

namespace TreeViewSpike
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
            List = new List<ItemList>
                       {
                           new ItemList
                               {
                                   Name = "MyList",
                                   Items = new List<Item> {new Item("1"),
                                                           new Item("2")}
                               },
                           new ItemList
                               {
                                   Name = "MySecondList",
                                   Items = new List<Item> {new Item("3"),
                                                           new Item("4")}
                               }
                       };
            Tree.DataContext = List;
            List[1].IsSelected = true;
        }

        public List<ItemList> List { get; set; }
    }

    public class ItemList: INotifyPropertyChanged
    {
        public string Name{ get; set;}
        private bool _isSelected;
        public bool IsSelected
        {
            get { return _isSelected; }
            set
            {
                _isSelected = value;
                if (PropertyChanged != null)
                    PropertyChanged(this,
                        new PropertyChangedEventArgs("IsSelected"));
                if(_isSelected)
                   MessageBox.Show(Name + " selected");
            }
        }

        public List<Item> Items { get; set; }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class Item
    {
        public string SomeData { get; set; }
        public Item(string data)
        {
            SomeData = data;
        }
    }
}
Dabblernl