TreeView与DragDrop和HierarchicalDataTemplate以及其他控件元素作为TreeViewItem

时间:2014-08-15 21:46:11

标签: c# wpf

我试图让TreeView工作,使用HierarchialDataTemplate将节点绑定到ObservableCollection。 TreeViewItems是带有复选框和组合框的网格。所有这些都有点工作,但我没有设法让拖放功能正常工作。

代码基于 TreeView, HierarchicalDataTemplate and recursive Datahttp://www.codeproject.com/Articles/55168/Drag-and-Drop-Feature-in-WPF-TreeView-Control

<Window x:Class="TreeView_HierarchicalDataTemplate.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:TreeView_HierarchicalDataTemplate="clr-namespace:TreeView_HierarchicalDataTemplate"
    Title="MainWindow" x:Name="mainWindow" Height="350" Width="525">

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>


<Grid>

    <TreeView Name="TreeView_After" AllowDrop="True" DataContext="{Binding ElementName=mainWindow, Path=TreeModel}" ItemsSource="{Binding Items}">

        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <EventSetter Event="TreeViewItem.DragOver" Handler="TreeView_After_DragOver"/>
                <EventSetter Event="TreeViewItem.Drop" Handler="TreeView_After_Drop"/>
                <EventSetter Event="TreeViewItem.MouseMove" Handler="TreeView_After_MouseMove"/>
                <EventSetter Event="TreeViewItem.MouseDown" Handler="TreeView_After_MouseDown"/>
            </Style>

        </TreeView.ItemContainerStyle>

        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type TreeView_HierarchicalDataTemplate:NodeViewModel}" ItemsSource="{Binding Children}">

                <Grid Background="LightBlue">
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}"></TextBlock>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
                    <CheckBox Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="1" Margin="5,5,0,0">activated</CheckBox>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
                    <StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
                        <TextBlock Margin="5">Action:</TextBlock>
                        <ComboBox>
                            <TextBlock>Move To</TextBlock>
                        </ComboBox>
                        <TextBlock Margin="5">C:\\Videos\Folder\aVideo.mkv</TextBlock>
                    </StackPanel>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="1" Width="5"/>
                    <StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="3" Orientation="Horizontal">
                        <TextBlock Margin="5">Duplicate of:</TextBlock>
                        <ComboBox>
                            <TextBlock>None</TextBlock>
                        </ComboBox>
                    </StackPanel>

                </Grid>

            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

</Grid>

这是Observable Collection

public class TreeViewModel
{
    public ObservableCollection<NodeViewModel> Items { get; set; }
}

public class NodeViewModel : UIElement
{
    public NodeViewModel()
    {
        Show = true;
    }

    public string Id { get; set; }
    public string Name { get; set; }
    public bool Show { get; set; }
    public ObservableCollection<NodeViewModel> Children { get; set; }
}

但是这个鼠标按下事件无法正常工作

private void TreeView_After_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left)
        {
            _lastMouseDown = e.GetPosition(TreeView_After);
        }

    }

此外,如果我从UIElement派生NodeViewModel,我只能从元素(UIElement)到容器(NodeViewModel)进行类型转换。但是,如果我从UIElement派生NodeViewModel,则TreeView根本不再显示:(

private NodeViewModel GetNearestContainer(UIElement element)
    {
        // Walk up the element tree to the nearest tree view item.
        NodeViewModel container = element as NodeViewModel;
        while ((container == null) && (element != null))
        {
            element = VisualTreeHelper.GetParent(element) as UIElement;
            container = element as NodeViewModel;
        }
        return container;
    }

1 个答案:

答案 0 :(得分:0)

我得到了Rohit Vats的一些帮助,就像这样:

MainWindow.xaml:

<Window x:Class="TreeView_HierarchicalDataTemplate.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:TreeView_HierarchicalDataTemplate="clr-namespace:TreeView_HierarchicalDataTemplate"
    Title="MainWindow" x:Name="mainWindow" Height="350" Width="525">

<Window.Resources>
    <BooleanToVisibilityConverter x:Key="BoolToVis" />
</Window.Resources>


<Grid>

    <TreeView Name="TreeView_After" AllowDrop="True" DataContext="{Binding ElementName=mainWindow, Path=TreeModel}" ItemsSource="{Binding Items}">

        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <EventSetter Event="TreeViewItem.DragOver" Handler="TreeView_After_DragOver"/>
                <EventSetter Event="TreeViewItem.Drop" Handler="TreeView_After_Drop"/>
                <EventSetter Event="TreeViewItem.MouseMove" Handler="TreeView_After_MouseMove"/>
            </Style>

        </TreeView.ItemContainerStyle>

        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type TreeView_HierarchicalDataTemplate:NodeViewModel}" ItemsSource="{Binding Children}">

                <Grid Background="LightBlue" MouseDown="TreeView_After_MouseDown">
                    <Grid.RowDefinitions>
                        <RowDefinition/>
                    </Grid.RowDefinitions>

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition/>
                    </Grid.ColumnDefinitions>

                    <TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}"></TextBlock>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
                    <CheckBox Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="1" Margin="5,5,0,0">activated</CheckBox>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="0" Width="5"/>
                    <StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="2" Orientation="Horizontal">
                        <TextBlock Margin="5">Action:</TextBlock>
                        <ComboBox>
                            <TextBlock>Move To</TextBlock>
                        </ComboBox>
                        <TextBlock Margin="5">C:\\Videos\Folder\aVideo.mkv</TextBlock>
                    </StackPanel>
                    <GridSplitter Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Column="1" Width="5"/>
                    <StackPanel Visibility="{Binding Path=Show, Converter={StaticResource BoolToVis}}" Grid.Row="0" Grid.Column="3" Orientation="Horizontal">
                        <TextBlock Margin="5">Duplicate of:</TextBlock>
                        <ComboBox>
                            <TextBlock>None</TextBlock>
                        </ComboBox>
                    </StackPanel>

                </Grid>

            </HierarchicalDataTemplate>
        </TreeView.Resources>
    </TreeView>

</Grid>

TreeViewModel:

public class TreeViewModel
{
    public ObservableCollection<NodeViewModel> Items { get; set; }
}

public class NodeViewModel
{
    public NodeViewModel()
    {
        Show = true;
    }

    public string Id { get; set; }
    public string Name { get; set; }
    public bool Show { get; set; }
    public ObservableCollection<NodeViewModel> Children { get; set; }
}

MainWindow代码:

public partial class MainWindow : Window
{
    TreeViewModel TreeViewModel_After;


    public MainWindow()
    {
        TreeViewModel_After = new TreeViewModel();

        TreeViewModel_After.Items = new ObservableCollection<NodeViewModel>{
                       new NodeViewModel { Name = "Root", Children =  new ObservableCollection<NodeViewModel> {
                          new NodeViewModel { Name = "Level1" ,  Children = new ObservableCollection<NodeViewModel>{ 
                              new NodeViewModel{ Name = "Level2"}}} } }};

        InitializeComponent();


    }

    public TreeViewModel TreeModel
    {
        get
        {
            return TreeViewModel_After;
        }
    }





    Point _lastMouseDown;
    NodeViewModel draggedItem, _target;


    private void TreeView_After_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Left)
        {
            _lastMouseDown = e.GetPosition(TreeView_After);
        }

    }


    private bool CheckGridSplitter(UIElement element)
    {
        if (element is GridSplitter)
        {
            return true;
        }

        GridSplitter GridSplitter = FindParent<GridSplitter>(element);

        if (GridSplitter != null)
        {
            return true;
        }
        return false;

    }


    private void TreeView_After_MouseMove(object sender, MouseEventArgs e)
    {
        try
        {
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                UIElement element = e.OriginalSource as UIElement;
                bool bGridSplitter = CheckGridSplitter(element);


                Point currentPosition = e.GetPosition(TreeView_After);


                if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                    (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
                {
                    draggedItem = (NodeViewModel)TreeView_After.SelectedItem;

                    if ( (draggedItem != null) && !bGridSplitter)
                    {
                        DragDropEffects finalDropEffect = DragDrop.DoDragDrop(TreeView_After, TreeView_After.SelectedValue,
                            DragDropEffects.Move);
                        //Checking target is not null and item is dragging(moving)
                        if ((finalDropEffect == DragDropEffects.Move) && (_target != null))
                        {

                            // A Move drop was accepted
                            if (draggedItem.Name != _target.Name)
                            {
                                CopyItem(draggedItem, _target);
                                _target = null;
                                draggedItem = null;
                            }      


                        }
                    }
                }
            }
        }
        catch (Exception)
        {
        }
    }
    private void TreeView_After_DragOver(object sender, DragEventArgs e)
    {
        try
        {

            Point currentPosition = e.GetPosition(TreeView_After);


            if ((Math.Abs(currentPosition.X - _lastMouseDown.X) > 10.0) ||
                (Math.Abs(currentPosition.Y - _lastMouseDown.Y) > 10.0))
            {
                // Verify that this is a valid drop and then store the drop target
                NodeViewModel item = GetNearestContainer(e.OriginalSource as UIElement);
                if (CheckDropTarget(draggedItem, item))
                {
                    e.Effects = DragDropEffects.Move;
                }
                else
                {
                    e.Effects = DragDropEffects.None;
                }
            }
            e.Handled = true;
        }
        catch (Exception)
        {
        }
    }
    private void TreeView_After_Drop(object sender, DragEventArgs e)
    {
        try
        {
            e.Effects = DragDropEffects.None;
            e.Handled = true;

            // Verify that this is a valid drop and then store the drop target
            NodeViewModel TargetItem = GetNearestContainer(e.OriginalSource as UIElement);
            if (TargetItem != null && draggedItem != null)
            {
                _target = TargetItem;
                e.Effects = DragDropEffects.Move;

            }
        }
        catch (Exception)
        {
        }



    }

    private bool CheckDropTarget(NodeViewModel _sourceItem, NodeViewModel _targetItem)
    {
        //Check whether the target item is meeting your condition
        bool _isEqual = false;

        if(_sourceItem.Name != _targetItem.Name )
        {
            _isEqual = true;
        }

        return _isEqual;

    }


    private void CopyItem(NodeViewModel _sourceItem, NodeViewModel _targetItem)
    {

        //Asking user wether he want to drop the dragged TreeViewItem here or not
        if (MessageBox.Show("Would you like to drop " + _sourceItem.Name + " into " + _targetItem.Name + "", "", MessageBoxButton.YesNo) == MessageBoxResult.Yes)
        {
            try
            {
                _targetItem.Children.Add(_sourceItem);
            }
            catch (Exception)
            {

            }
        }

    }


    private NodeViewModel GetNearestContainer(UIElement element)
    {
        // Walk up the element tree to the nearest tree view item.
        TreeViewItem UIContainer = FindParent<TreeViewItem>(element);
        NodeViewModel NVContainer = null;

        if (UIContainer != null)
        {
            NVContainer = UIContainer.DataContext as NodeViewModel;
        }
        return NVContainer;
    }


    private static Parent FindParent<Parent>(DependencyObject child)
            where Parent : DependencyObject
    {
        DependencyObject parentObject = child;
        parentObject = VisualTreeHelper.GetParent(parentObject);

        //check if the parent matches the type we're looking for
        if (parentObject is Parent || parentObject == null)
        {
            return parentObject as Parent;
        }
        else
        {
            //use recursion to proceed with next level
            return FindParent<Parent>(parentObject);
        }
    }

}