如何处理Tree View WPF MVVM的ItemChanged事件

时间:2016-06-26 04:56:42

标签: wpf mvvm

大部分时间我们在处理树的ItemChange或SelectionChanged时遇到问题。经过大量的努力,我找到了适合自己的解决方案。以下是答案

1 个答案:

答案 0 :(得分:0)

使用下面附加的属性当选择树项或更改选择时,它会直接在该对象上引发命令

public class ControlBehaviour
{
    private static IDictionary<object, ICommand> dataContextCommandMap = new Dictionary<object, ICommand>();
    private static IDictionary<FrameworkElement, object> elementDataConextMap = new Dictionary<FrameworkElement, object>();
    /// <summary>
    /// 
    /// </summary>
    public static readonly DependencyProperty ControlEventProperty =
        DependencyProperty.RegisterAttached("ControlEvent", typeof(RoutedEvent), typeof(ControlBehaviour),
        new PropertyMetadata(OnTreeviewSelectionChanged));

    /// <summary>
    /// 
    /// </summary>
    /// <param name="target"></param>
    /// <param name="value"></param>
    public static void SetControlEvent(DependencyObject target, object value)
    {
        target.SetValue(ControlEventProperty, value);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <returns></returns>
    public static RoutedEvent GetControlEvent(DependencyObject sender)
    {
        return sender.GetValue(ControlEventProperty) as RoutedEvent;
    }
    /// <summary>
    /// Command to be executed
    /// </summary>
    public static readonly DependencyProperty CommandProperty =
        DependencyProperty.RegisterAttached("Command", typeof(ICommand),
        typeof(ControlBehaviour), new PropertyMetadata(CommandChanged));
    /// <summary>
    /// Set ICommand to the dependency object
    /// </summary>
    /// <param name="target">dependency object</param>
    /// <param name="value">I command</param>
    public static void SetCommand(DependencyObject target, object value)
    {
        target.SetValue(CommandProperty, value);
    }
    /// <summary>
    /// Get ICommand to the dependency object
    /// </summary>
    /// <param name="sender">dependency object</param>
    /// <returns>ICommand of the dependency object</returns>
    public static ICommand GetCommand(DependencyObject sender)
    {
        return sender.GetValue(CommandProperty) as ICommand;
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    static void OnTreeviewSelectionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender != null)
        {
            TreeView element = sender as TreeView;
            if (element != null)
            {
                if (e.NewValue != null)
                {
                    element.SelectedItemChanged += Handler;
                }
                if (e.OldValue != null)
                {
                    element.SelectedItemChanged -= Handler;
                }
            }
        }
    }
    /// <summary>
    /// ICommand Changed
    /// </summary>
    /// <param name="sender">dependency object</param>
    /// <param name="e">dependency args</param>
    private static void CommandChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (sender != null)
        {
            FrameworkElement cntrl = sender as FrameworkElement;
            if (cntrl != null && cntrl.DataContext != null)
            {
                if (e.NewValue != null)
                {
                    ICommand cmd = e.NewValue as ICommand;
                    if (cmd != null)
                    {
                        elementDataConextMap[cntrl] = cntrl.DataContext;
                        dataContextCommandMap[cntrl.DataContext] = cmd;
                    }
                }
                cntrl.Unloaded += FrameworkElementUnloaded;
            }
        }
    }
    /// <summary>
    /// Framework element unload, Clears the removes the ICommand
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private static void FrameworkElementUnloaded(object sender, RoutedEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element != null && elementDataConextMap.ContainsKey(element))
        {
            dataContextCommandMap.Remove(elementDataConextMap[element]);
            elementDataConextMap.Remove(element);
        }
    }
    /// <summary>
    /// Routed Event Handler
    /// </summary>
    /// <param name="sender">sender</param>
    /// <param name="e">EventArgs</param>
    static void Handler(object sender, EventArgs e)
    {
        TreeView treeView = sender as TreeView;
        if (treeView != null && treeView.SelectedItem != null && dataContextCommandMap.ContainsKey(treeView.SelectedItem)
            && dataContextCommandMap[treeView.SelectedItem].CanExecute(treeView.SelectedItem))
        {
            dataContextCommandMap[treeView.SelectedItem].Execute(treeView.SelectedItem);
        }
    }
}

以下是使用上述附加属性的XMAL

   <TreeView Grid.Row="1" Background="Transparent" ItemsSource="{Binding Directories}" Margin="0,10,0,0" Name="FolderListTreeView"
                Height="Auto" HorizontalAlignment="Stretch" Width="300"  local:ControlBehaviour.ControlEvent="TreeView.SelectedItemChanged" >
                <TreeView.Resources>
                    <HierarchicalDataTemplate DataType="{x:Type local:FileSystem}" ItemsSource="{Binding SubDirectories}">
                        <Label Content="{Binding Path= Name}" Name="NodeLabel" local:ControlBehaviour.Command="{Binding OnSelect}"/>
                    </HierarchicalDataTemplate>
                </TreeView.Resources>
            </TreeView>

下面是绑定在Xaml中的FileSystem类

 /// <summary>
/// Implementation of file system class
/// </summary>

  public class FileSystem :BindableBase, IFileSystem, IEnumerable //BindableBase is base class which implemets INotifyPropertyChanged
{
    #region Private members
    private ObservableCollection<IFileSystem> subDirectoriesField;
    private ObservableCollection<IFileSystem> filesField;
    #endregion

    #region Public properties
    /// <summary>
    /// Gets and sets all the Files in the current folder
    /// </summary>
    public ObservableCollection<IFileSystem> SubDirectories
    {
        get
        {
            return subDirectoriesField;
        }
        set
        {
            if (subDirectoriesField != value)
            {
                subDirectoriesField = value;
                NotifyPropertyChanged("SubDirectories");
            }
        }
    }
    /// <summary>
    /// Gets and sets all the files in the current folder 
    /// </summary>
    public ObservableCollection<IFileSystem> Files
    {
        get
        {
            return filesField;
        }
        set
        {
            if (filesField != value)
            {
                filesField = value;
                RaisePropertyChanged("Files");
            }
        }
    }
    /// <summary>
    /// Gets or sets the type of the file
    /// </summary>
    public FileSystemType FileType
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets the size of the file
    /// </summary>
    public long Size
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets name of the file system 
    /// </summary>
    public string Name
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets full path of the file system
    /// </summary>
    public string FullPath
    {
        get;
        set;
    }
    /// <summary>
    /// object of parent, null if the current node is root
    /// </summary>
    public FileSystem Parent
    {
        get;
        set;
    }
    /// <summary>
    /// Returns ICommand
    /// </summary>
    public ICommand OnSelect
    {
        get
        {
            return new Command_R(Execute);//Command_R is implemention of your ICommand
        }
    }
    #endregion

    #region Constructor
    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="path">path of the folder</param>
    /// <param name="parent">object of the parent</param>
    public FileSystem(string fullPath, FileSystem parent, FileSystemType
        type = FileSystemType.Directory)
    {
        Name = fullPath != null ? GetNameFileName(fullPath) : fullPath;
        FullPath = fullPath;
        Parent = parent;
        FileType = type;
        FilesAndSubDirectoriesDetails(fullPath);
    }

    #endregion

    #region Public methods
    /// <summary>
    /// Updates the file size if there is a change
    /// </summary>
    /// <param name="deleteFilzeSize"></param>
    public void UpdateFileSize(long deleteFilzeSize)
    {
        UpdatePredecessor(this, deleteFilzeSize);
    }
    /// <summary>
    /// Gets the enumeration list
    /// </summary>
    /// <returns>returns the enumeration list</returns>
    public IEnumerator GetEnumerator()
    {
        return SubDirectories.GetEnumerator();
    }
    #endregion

    #region Private methods
    /// <summary>
    /// Finds the details of the files and sub directories
    /// </summary>
    /// <param name="fullPath"></param>
    private void FilesAndSubDirectoriesDetails(string fullPath)
    {
        if (FileType.Equals(FileSystemType.Directory))
        {
            AddFilesAndSubDirectories(fullPath);
            CalculateDirectorySize();
        }
        else
        {
            //Write code to calcuate the File Size
            //Size = FileInfo.GetSize(fullPath);
        }

    }
    /// <summary>
    /// Finds and adds the files and sub directories
    /// </summary>
    /// <param name="fullPath"></param>
    private void AddFilesAndSubDirectories(string fullPath)
    {

        string[] subDirectories = Directory.GetDirectories(fullPath);
        SubDirectories = new ObservableCollection<IFileSystem>();
        foreach (string directory in subDirectories)
        {
            SubDirectories.Add(new FileSystem(directory, this));
        }
        Files = new ObservableCollection<IFileSystem>();
        string[] files = File.GetFiles(fullPath);
        foreach (string fileName in files)
        {
            Files.Add(new FileSystem(fileName, this, FileSystemType.File));
        }

    }
    /// <summary>
    /// Calculates the current directory size
    /// </summary>
    private void CalculateDirectorySize()
    {
        foreach (FileSystem directory in SubDirectories)
        {
            Size += directory.Size;
        }
        foreach (FileSystem file in Files)
        {
            Size += file.Size;
        }

    }
    /// <summary>
    /// Updates the file size of the predecessors 
    /// </summary>
    /// <param name="currentNode">current node</param>
    /// <param name="deleteFilzeSize">file to be updated</param>
    private void UpdatePredecessor(FileSystem currentNode, long deletedFilzeSize)
    {
        if (currentNode != null)
        {
            currentNode.Size -= deletedFilzeSize;
            UpdatePredecessor(currentNode.Parent, deletedFilzeSize);
        }
    }
    /// <summary>
    /// Executes ICommand
    /// </summary>
    /// <param name="parameter">parameter</param>
    private void Execute(object parameter)
    {
        //Do your Job
        MessageBox.Show(FullPath);
    }
    #endregion
}

和班级的接口

/// <summary>
/// Type of the file
/// </summary>
public enum FileSystemType
{
    /// <summary>
    /// File
    /// </summary>
    File,
    /// <summary>
    /// Directory
    /// </summary>
    Directory
}
/// <summary>
/// Interface for File system
/// </summary>
public interface IFileSystem
{
    /// <summary>
    /// Gets or sets file type
    /// </summary>
    FileSystemType FileType
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets the file size
    /// </summary>
    long Size
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets name of the file system 
    /// </summary>
    string Name
    {
        get;
        set;
    }
    /// <summary>
    /// Gets or sets full path of the file system
    /// </summary>
    string FullPath
    {
        get;
        set;
    }
}