cjw
cjw

Reputation: 354

WPF MVVM Update text editor when TreeViewItem selection changes

I have a TreeView of DirectoryItems that are defined like this:

public class DirectoryItem : INotifyPropertyChanged
{
    public String DisplayName { get; set; }
    public String Fullpath { get; set; }
    public ObservableCollection<DirectoryItem> Children { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
}

When the user selects a DirectoryItem from the treeView, I want to take the Fullpath from that item, read all the text from the file, and load it into a text editor.

My ViewModel has a DisplayText property that the text editor is bound to:

internal class MainWindowViewModel : IMainWindowViewModel
{
    private ObservableCollection<DirectoryItem> DirectoryItems { get; set; }

    public string DisplayText { get; set; }
    //this needs to be implemented to take a filepath and read from it
}

I think there should be a command called that is associated to the TreeViewItem's IsSelected property that passes the Fullpath to the ViewModel, but I haven't been able to get the code working. How would I implement this?

Here's what I have that I can't get working, I get a "Property triggers is not attachable to elements of type Style" error:

<Style TargetType="{x:Type TreeViewItem}">
    <i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectedItemChanged">
                    <i:InvokeCommandAction Command="{Binding DisplayText}" CommandParameter="{Binding ElementName=treeView, Path=SelectedItem}"/>
                </i:EventTrigger>
    </i:Interaction.Triggers>
</Style>

Thank you.

Upvotes: 0

Views: 258

Answers (1)

mm8
mm8

Reputation: 169228

You can't add an interaction trigger inside a Style.

What you could do is to add an IsSelected property to your DirectoryItem class and bind the IsSelected property of the TreeViewItem to this one:

<Style TargetType="{x:Type TreeViewItem}">
    <Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>

public class DirectoryItem : INotifyPropertyChanged
{
    public String DisplayName { get; set; }
    public String Fullpath { get; set; }
    public ObservableCollection<DirectoryItem> Children { get; set; }

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

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Make sure that you actually raise the PropertyChanged property whenever the IsSelected is set to a new value.

The view model could then hook up to the PropertyChanged event of each item in the ObservableCollection<DirectoryItem> and do something whenever an item is selected:

internal class MainWindowViewModel : IMainWindowViewModel
{
    public MainWindowViewModel()
    {
        //add the items...
        DirectoryItems.Add(new DirectoryItem() { DisplayName = "test", Children = new ObservableCollection<DirectoryItem>() { new DirectoryItem() { DisplayName = "child" } } });

        foreach(var item in DirectoryItems)
        {
            item.PropertyChanged += Item_PropertyChanged;
        }
    }

    private void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if(e.PropertyName == "IsSelected")
        {
            DirectoryItem selectedItem = sender as DirectoryItem;
            //set the DisplayText property
        }
    }

    public ObservableCollection<DirectoryItem> DirectoryItems { get; set; } = new ObservableCollection<DirectoryItem>();

    public string DisplayText { get; set; }
}

If you are adding items dynamically to the ObservableCollection<DirectoryItem> you probably also want to handle the CollectionChanged event to hook up event handlers to any new objects as well:

private void DirectoryItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach (object item in e.NewItems)
        {
            (item as INotifyPropertyChanged).PropertyChanged
                += new PropertyChangedEventHandler(Item_PropertyChanged);
        }
    }

    if (e.OldItems != null)
    {
        foreach (object item in e.OldItems)
        {
            (item as INotifyPropertyChanged).PropertyChanged
                -= new PropertyChangedEventHandler(Item_PropertyChanged);
        }
    }
}

Upvotes: 1

Related Questions