Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieving bound object on node from TreeView

I'm not an expert in WPF so forgive me if I am wording my question weirdly. I'd be more than happy to elaborate if anything doesn't make sense.

I have a treeview that binds an observablecollection of a class. When my program launches, I read every C sourcecode files at a particular destination, store its name and filepath in the class mentioned.

enter image description here

Here is my XAML:

<TreeView Name="ProgramTree" ItemsSource="{Binding ProgramItemCollection}" 
                                      cal:Message.Attach="[Event PreviewMouseRightButtonDown] = [Action TestRight($dataContext,$eventArgs)];
                                      [Event PreviewMouseDoubleClick] = [Action NodeDoubleClick($dataContext,$eventArgs)]">

    <TreeView.Resources>
        <!--DataTemplate for Program Nodes (Top) and binds FileItemNodes-->
        <HierarchicalDataTemplate DataType="{x:Type my:ProgramItem}"
                                        ItemsSource="{Binding FileItemCollection}">
            <Border Width="100" BorderBrush="RoyalBlue" 
                                            Background="RoyalBlue"  BorderThickness="1" 
                                            CornerRadius="2" Margin="2" Padding="2" >
                <StackPanel Orientation="Horizontal">
                    <Image Style="{StaticResource IconStyle}" Margin="2" Source="{StaticResource FolderIcon}" />
                    <TextBlock Margin="2" Text="{Binding ProgramName}"
                                                           Foreground="White" FontWeight="Bold"/>
                </StackPanel>
            </Border>
        </HierarchicalDataTemplate>
        <!--DataTemplate for File Nodes (Subnodes of Program Nodes)-->
        <HierarchicalDataTemplate DataType="{x:Type my:FileItem}">
            <Border Width="80"  Background="LightBlue" CornerRadius="2" Margin="1" >
                <StackPanel Orientation="Horizontal">
                    <Image Margin="2" />
                    <TextBlock Margin="2" Text="{Binding NodeName}" />
                </StackPanel>
            </Border>
        </HierarchicalDataTemplate>
    </TreeView.Resources>

Codebehind:

public class FileItem
{
    public string NodeName { get; set; }
    public string FullName { get; set; }
    public string Extension { get; set; }
}

public class ProgramItem : PropertyChangedBase
{
    private ObservableCollection<FileItem> fileItemCollection;
    ...

What I now want to do is hook a double click event on the node and open the relevant file.

    public void NodeDoubleClick(object sender, MouseButtonEventArgs e)
    {
        TreeViewItem treeViewItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);

        if (treeViewItem != null)
        {
            //Open file
        }
    }

    private static TreeViewItem VisualUpwardSearch(DependencyObject source)
    {
        while (source != null && !(source is TreeViewItem))
            source = VisualTreeHelper.GetParent(source);

        return source as TreeViewItem;
    }

I can retrieve the double clicked node (treeviewitem) without a problem. The problem is I want to retrieve an object of FileItem from the node I double clicked to access the filepath property. Is this possible at all?

like image 874
l46kok Avatar asked Jan 16 '23 12:01

l46kok


1 Answers

It is possible by resolving the DataContext of the TreeViewItem:

FileItem fileItem = (treeViewItem.DataContext as FileItem);

A more elegant way would be to use MouseInput Bindings and a Command in your FileItem class.

In your Datatemplate for FileItem:

<StackPanel Orientation="Horizontal">
    <StackPanel.InputBindings>
        <MouseBinding MouseAction="LeftDoubleClick" 
                      Command="{Binding OpenFileCommand}" />
    </StackPanel.InputBindings>
    <Image Margin="2" />
    <TextBlock Margin="2" Text="{Binding NodeName}" />
</StackPanel>

In your FileItem:

public class FileItem
{
   public FileItem()
   {
       this.OpenFileCommand 
           = new SimpleCommand(()=> Process.StartNew(this.FullName));
   }

   public string NodeName { get; set; }
   public string FullName { get; set; }
   public string Extension { get; set; }
   public ICommand OpenFileCommand { get; set;}
}

P.S.: If you are not used to WPF's Commands, a basic implementation of a simple ICommand could be:

public class SimpleCommand : System.Windows.Input.ICommand
{
    public SimpleCommand(Action action)
    {
        this.Action = action;
    }

    public Action Action { get; set; }

    public bool CanExecute(object parameter)
    {
        return (this.Action != null);
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (this.Action != null)
        {
            this.Action();
        }
    }
}

Commands are much more effective for such szenarios. You dont need to walk the visual Tree and you do not need code behind at all.

like image 165
JanW Avatar answered Jan 29 '23 02:01

JanW