user5845587
user5845587

Reputation:

Treeview IsExpanded not fires up

I am trying to add to the TreeView the ability to catch the IsExpanded event. So that when a certain Item will expand it will raise a property or command on the view model.

Here is my code:

  <TreeView  Name="ScenariosTreeView" ItemsSource="{Binding Path=Cat, Mode=TwoWay}" Focusable="True" >
     <TreeView.InputBindings>
       <KeyBinding Key="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedValue ,ElementName=ScenariosTreeView}" />
     </TreeView.InputBindings>

     <TreeView.ItemContainerStyle>
       <Style TargetType="{x:Type TreeViewItem}">
         <Setter Property="IsExpanded" Value="{Binding IsExtended, Mode=TwoWay}" />
         <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
       </Style>
     </TreeView.ItemContainerStyle>

     <TreeView.Resources> 
       <HierarchicalDataTemplate DataType="{x:Type sotc:ScenarioCategory}" ItemsSource="{Binding Path=ScenarioList}" >
         <TextBlock  Text="{Binding Path=Name}" Foreground="Black" IsEnabled="True" Focusable="True"/>
       </HierarchicalDataTemplate>

       <DataTemplate DataType="{x:Type sotc:Scenario}" >
         <TextBlock  Text="{Binding Path=Name, Mode=TwoWay}" />
       </DataTemplate>
     </TreeView.Resources> 
  </TreeView>

I also have a property in the viewmodel for IsExpanded (and for IsSelected) and none of that raises when I am expand the TreeviewItem. Any Ideas?

Thanks ahead,

Upvotes: 1

Views: 1004

Answers (2)

user5845587
user5845587

Reputation:

Found the answer. It fired the event if it is placed in the object represented as a treeviewItem and not in the view model.

Upvotes: 0

Anton Danylov
Anton Danylov

Reputation: 1491

Tried to reproduce the issue and implemented attached behavior for calling command when node is expanded and everything works fine. Complete code for the solution:

XAML

<Window x:Class="TreeViewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:sotc="clr-namespace:TreeViewTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <sotc:MainWindowViewModel />
    </Window.DataContext>
    <Grid>
        <TreeView  Name="ScenariosTreeView" ItemsSource="{Binding Path=Cat, Mode=TwoWay}" Focusable="True" >
            <TreeView.InputBindings>
                <KeyBinding Key="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedValue ,ElementName=ScenariosTreeView}" />
            </TreeView.InputBindings>

            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExtended, Mode=TwoWay}" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
                    <Setter Property="sotc:Behaviours.ExpandingBehaviour" Value="{Binding DataContext.ExpandingCommand, RelativeSource={RelativeSource AncestorType=Window}}"/>
                </Style>
            </TreeView.ItemContainerStyle>

            <TreeView.Resources>
                <HierarchicalDataTemplate DataType="{x:Type sotc:ScenarioCategory}" ItemsSource="{Binding Path=ScenarioList}" >
                    <TextBlock  Text="{Binding Path=Name}" Foreground="Black" IsEnabled="True" Focusable="True"/>
                </HierarchicalDataTemplate>

                <DataTemplate DataType="{x:Type sotc:Scenario}" >
                    <TextBlock  Text="{Binding Path=Name, Mode=TwoWay}" />
                </DataTemplate>
            </TreeView.Resources>
        </TreeView>
    </Grid>
</Window>

C#

public class ViewModelBase : INotifyPropertyChanged
{

    public void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class MainWindowViewModel : ViewModelBase
{
    public ICommand ExpandingCommand { get; set; }

    private void ExecuteExpandingCommand(object obj)
    {
        Console.WriteLine(@"Expanded");
    }
    private bool CanExecuteExpandingCommand(object obj)
    {
        return true;
    }

    public MainWindowViewModel()
    {
        ExpandingCommand = new RelayCommand(ExecuteExpandingCommand, CanExecuteExpandingCommand);

        Cat = new ObservableCollection<ScenarioCategory>(new ScenarioCategory[] 
        {
            new ScenarioCategory { Name = "C1" }, new ScenarioCategory { Name = "C2" }, new ScenarioCategory { Name = "C3" }
        });
    }

    public ObservableCollection<ScenarioCategory> Cat { get; set; }
}


public class ScenarioCategory : ViewModelBase
{
    string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

    bool _isExtended;
    public bool IsExtended
    {
        get { return _isExtended; }
        set
        {
            _isExtended = value;
            Console.WriteLine(@"IsExtended set");
            OnPropertyChanged();
        }
    }

    bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            Console.WriteLine(@"IsSelected set");
            OnPropertyChanged();
        }
    }

    public ObservableCollection<Scenario> ScenarioList { get; set; }

    public ScenarioCategory()
    {
        ScenarioList = new ObservableCollection<Scenario>(new Scenario[] { new Scenario { Name = "1" }, new Scenario { Name = "2" }, new Scenario { Name = "3" } });
    }

}

public class Scenario : ViewModelBase
{
    string _name;
    public string Name
    {
        get { return _name; }
        set { _name = value; OnPropertyChanged(); }
    }

    bool _isExtended;
    public bool IsExtended
    {
        get { return _isExtended; }
        set
        {
            _isExtended = value;
            OnPropertyChanged();
        }
    }

    bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            _isSelected = value;
            OnPropertyChanged();
        }
    }
}


public static class Behaviours
{
    public static readonly DependencyProperty ExpandingBehaviourProperty =
        DependencyProperty.RegisterAttached("ExpandingBehaviour", typeof(ICommand), typeof(Behaviours),
            new PropertyMetadata(OnExpandingBehaviourChanged));


    public static void SetExpandingBehaviour(DependencyObject o, ICommand value)
    {
        o.SetValue(ExpandingBehaviourProperty, value);
    }
    public static ICommand GetExpandingBehaviour(DependencyObject o)
    {
        return (ICommand)o.GetValue(ExpandingBehaviourProperty);
    }
    private static void OnExpandingBehaviourChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        TreeViewItem tvi = d as TreeViewItem;
        if (tvi != null)
        {
            ICommand ic = e.NewValue as ICommand;
            if (ic != null)
            {
                tvi.Expanded += (s, a) =>
                {
                    if (ic.CanExecute(a))
                    {
                        ic.Execute(a);

                    }
                    a.Handled = true;
                };
            }
        }
    }
}


public class RelayCommand : ICommand
{
    private Action<object> execute;
    private Func<object, bool> canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }
}

Upvotes: 2

Related Questions