0

I try to add one method located into TreeViewModel.TreeViewItem_Expanded:

public void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = e.Source as TreeViewItem;
            if ((item.Items.Count == 1) && (item.Items[0] is string))
            {
                item.Items.Clear();

                DirectoryInfo expandedDir = null;
                if (item.Tag is DriveInfo)
                    expandedDir = (item.Tag as DriveInfo).RootDirectory;
                if (item.Tag is DirectoryInfo)
                    expandedDir = (item.Tag as DirectoryInfo);
                try
                {
                    foreach (DirectoryInfo subDir in expandedDir.GetDirectories())
                        item.Items.Add(CreateTreeItem(subDir));
                }
                catch { }
            }
        }

to TreeView into xaml:

 <Grid Background="MidnightBlue">
    <TreeView Name="trvStructure" TreeViewItem.Expanded="TreeViewModel.TreeViewItem_Expanded" Margin="20" />
 </Grid>

I tried also to add namespace:

 xmlns:Treemodels="clr-namespace:LayoutMVVM.ViewModels"

and then:

<TreeView Name="trvStructure" TreeViewItem.Expanded="Treemodels.TreeViewModel.TreeViewItem_Expanded" Margin="20" />

But also did not work. Error:

enter image description here

4est
  • 3,010
  • 6
  • 41
  • 63
  • What doesn't work? Do you get exceptions (compiler/run-time errors)? Binding errors (check Output window)? – Sinatr May 16 '17 at 09:53
  • Added screen from error: 'Treemodels.TreeViewModel.TreeViewItem_Expanded' is not a valid event handler method name. Only instance methods on the generated or code-behind class are valid. – 4est May 16 '17 at 09:56
  • Event handlers are typically located in `xaml.cs`-file, right click `xaml`-file and choose "View code", put event handler there. – Sinatr May 16 '17 at 09:57
  • I know that I can move it to xaml.cs, but I was thinking if it's possible to connect from other class – 4est May 16 '17 at 10:03
  • Consider using [alternative approach](http://stackoverflow.com/q/1717654/1997232), where you provide a property for item viewmodel to control expanded state. In the setter you can run additional logic (in your case run model method to fill current item with children - directories). – Sinatr May 16 '17 at 10:11

1 Answers1

2

Answer is updated:

OK, here is the full answer:

XAML:

<TreeView Name="trvStructure" Margin="20" ItemsSource="{Binding Items}">
        <TreeView.Resources>
            <Style TargetType="TreeViewItem">
                <Setter Property="Header" Value="{Binding Name}"/>
                <Setter Property="ItemsSource" Value="{Binding Items}"/>
                <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
            </Style>
        </TreeView.Resources>
</TreeView>

ViewModel:

public class MainViewModel
{
    public ObservableCollection<TreeViewItemViewModel> Items { get; } = new ObservableCollection<TreeViewItemViewModel>();
    public MainViewModel()
    {
        foreach (var driveInfo in DriveInfo.GetDrives())
        {
            Items.Add(new DriveTreeViewItemViewModel(driveInfo));
        }
    }
}

public abstract class TreeViewItemViewModel
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private ICommand _expandedCommand;

    private bool _isExpanded;

    public string Name { get; }

    public bool IsExpanded
    {
        get
        {
            return _isExpanded;
        }
        set
        {
            TreeViewItemExpanded();
            _isExpanded = value;
            OnPropertyChanged(nameof(IsExpanded));
        }
    }

    public TreeViewItemViewModel(string name)
    {
        Name = name;
    }

    public ObservableCollection<TreeViewItemViewModel> Items { get; } = new ObservableCollection<TreeViewItemViewModel>();

    protected abstract void TreeViewItemExpanded();

    protected void OnTreeViewItemExpanded(DirectoryInfo info)
    {
        Items.Clear();

        foreach (DirectoryInfo subDir in info.GetDirectories())
        {
            Items.Add(new DirectoryTreeViewItemViewModel(subDir));
        }
    }
}

public class DummyTreeViewItemViewModel : TreeViewItemViewModel
{
    public DummyTreeViewItemViewModel() : base(string.Empty)
    {
    }

    protected override void TreeViewItemExpanded()
    {
    }
}

public class DirectoryTreeViewItemViewModel : TreeViewItemViewModel
{
    public DirectoryTreeViewItemViewModel(DirectoryInfo directory) : base(directory.Name)
    {
        Directory = directory;
        Items.Add(new DummyTreeViewItemViewModel());
    }

    public DirectoryInfo Directory { get; }


    protected override void TreeViewItemExpanded()
    {
        OnTreeViewItemExpanded(Directory);
    }
}

public class DriveTreeViewItemViewModel : TreeViewItemViewModel
{
    public DriveTreeViewItemViewModel(DriveInfo drive) : base(drive.Name)
    {
        Drive = drive;
        Items.Add(new DirectoryTreeViewItemViewModel(Drive.RootDirectory));
    }

    public DriveInfo Drive { get; }

    protected override void TreeViewItemExpanded()
    {
        OnTreeViewItemExpanded(Drive.RootDirectory);
    }
}

MainViewModel is a TreeView DateContext. Hope you can make it work.

Yevgeniy
  • 1,054
  • 1
  • 9
  • 17
  • Since event handler is using `TreeViewItem` it's bad idea to move it into viewmodel as it is. Rather use *dirty* MVVM (with event handlers for the view, which can simply call viewmodel method). – Sinatr May 16 '17 at 10:00
  • 4est could get rid of TreeViewItem in the event handler and use the underlying ViewModel (TreeViewItem DataContext) instead – Yevgeniy May 16 '17 at 10:15
  • Ye, passing `CommandParameter="{Binding DataContext, RelativeSource={RelativeSource Self}}"` as a part of `TreeViewItem` command binding might be ok in this case. My comment was more general, there is nothing bad to use event handlers at first in the view and that could be refactored later using attached behavior into *pure* MVVM. – Sinatr May 16 '17 at 10:20
  • I have create seperate class MainViewModel.cs, put your code there, and into my TreeView.xaml put xaml code, but it did not connect together – 4est May 17 '17 at 07:30
  • 1
    You should set MainViewModel as DataContext of your TreeView. If it is a separate xaml file, just add DataContext = new MainViewModel(); in the constructor in xaml.cs file. – Yevgeniy May 17 '17 at 08:51
  • thanks Yevgeniy! did as you said and now is working! – 4est May 17 '17 at 09:33