简体   繁体   中英

Dragging and dropping TreeViewItems in non-clickable area of TreeView [WPF]

A question about dragging and dropping tree view items in WPF.

My original question is a little complex. So I simplified it, and here is the code: XAML

<Window x:Class="WpfApplication2.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
<Grid>
    <TreeView>
        <TreeView.ItemContainerStyle>
            <Style>
                <Setter Property="TreeViewItem.IsExpanded" Value="True"/>
                <Setter Property="TreeViewItem.Background" Value="LightBlue"/>
                <Setter Property="TreeViewItem.AllowDrop" Value="True"/>
                <EventSetter Event="TreeViewItem.MouseMove" Handler="TreeNode_MouseMove"/>
                <EventSetter Event="TreeViewItem.Drop" Handler="TreeNode_Drop"/>
            </Style>
        </TreeView.ItemContainerStyle>

        <TreeViewItem Header="first node in the tree"/>
        <TreeViewItem Header="second node in the tree"></TreeViewItem>
        <TreeViewItem Header="third node in the tree"></TreeViewItem>
    </TreeView>
</Grid>

And code-behind:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }

    private void TreeNode_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed)
        {
            DragDrop.DoDragDrop(this, DateTime.Now.ToString(), DragDropEffects.Move);
        }
    }

    private void TreeNode_Drop(object sender, DragEventArgs e)
    {
        string str = (string)e.Data.GetData(typeof(string));
        MessageBox.Show(str);
    }
}

So what I want to do is very simple, just pop up a message box when one tree view item is dragged and dropped on another tree view item.

When I drop it right on another item, it works fine, like this:

在此输入图像描述

But if I drag it slightly off the boundary of the item, it does not work, like this:

在此输入图像描述

The mouse cursor is displayed as a “forbidden sign”, sorry I can't get it in the screen shot.

So now my question is: how to make the second condition work? How to make the Drop event still fire up when the dropping location is slightly off the boundary of the item?

Thanks in advance.

Take the default ControlTemplate of TreeViewItem and make the Border (the border which contains the ContentPresenter) to have a colspan of 2, eg:

<Border
            BorderThickness="{TemplateBinding Border.BorderThickness}"
            Padding="{TemplateBinding Control.Padding}"
            BorderBrush="{TemplateBinding Border.BorderBrush}"
            Background="{TemplateBinding Panel.Background}"
            Name="Bd"
            SnapsToDevicePixels="True"
            Grid.Column="1" Grid.ColumnSpan="2">

Note that the whole area in TreeViewItem next to the TextBlock would become clickable . You may want to play around with the template to fit it to your needs.
在此输入图像描述

Below is the ControlTemplate of TreeViewItem extracted using Dump Control Template Utility .

<?xml version="1.0" encoding="utf-16"?>
<ControlTemplate
    TargetType="TreeViewItem" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition
                Width="Auto"
                MinWidth="19" />
            <ColumnDefinition
                Width="Auto" />
            <ColumnDefinition
                Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <ToggleButton
            IsChecked="False"
            ClickMode="Press"
            Name="Expander">
            <ToggleButton.Style>
                <Style
                    TargetType="ToggleButton">
                    <Style.Resources>
                        <ResourceDictionary />
                    </Style.Resources>
                    <Setter
                        Property="UIElement.Focusable">
                        <Setter.Value>
                            <s:Boolean>False</s:Boolean>
                        </Setter.Value>
                    </Setter>
                    <Setter
                        Property="FrameworkElement.Width">
                        <Setter.Value>
                            <s:Double>19</s:Double>
                        </Setter.Value>
                    </Setter>
                    <Setter
                        Property="FrameworkElement.Height">
                        <Setter.Value>
                            <s:Double>13</s:Double>
                        </Setter.Value>
                    </Setter>
                    <Setter
                        Property="Control.Template">
                        <Setter.Value>
                            <ControlTemplate
                                TargetType="ToggleButton">
                                <Border
                                    Background="#00FFFFFF"
                                    Width="19"
                                    Height="13">
                                    <Border
                                        BorderThickness="1,1,1,1"
                                        CornerRadius="1,1,1,1"
                                        BorderBrush="#FF7898B5"
                                        Width="9"
                                        Height="9"
                                        SnapsToDevicePixels="True">
                                        <Border.Background>
                                            <LinearGradientBrush
                                                StartPoint="0,0"
                                                EndPoint="1,1">
                                                <LinearGradientBrush.GradientStops>
                                                    <GradientStop
                                                        Color="#FFFFFFFF"
                                                        Offset="0.2" />
                                                    <GradientStop
                                                        Color="#FFC0B7A6"
                                                        Offset="1" />
                                                </LinearGradientBrush.GradientStops>
                                            </LinearGradientBrush>
                                        </Border.Background>
                                        <Path
                                            Data="M0,2L0,3 2,3 2,5 3,5 3,3 5,3 5,2 3,2 3,0 2,0 2,2z"
                                            Fill="#FF000000"
                                            Name="ExpandPath"
                                            Margin="1,1,1,1" />
                                    </Border>
                                </Border>
                                <ControlTemplate.Triggers>
                                    <Trigger
                                        Property="ToggleButton.IsChecked">
                                        <Setter
                                            Property="Path.Data"
                                            TargetName="ExpandPath">
                                            <Setter.Value>
                                                <StreamGeometry>M0,2L0,3 5,3 5,2z</StreamGeometry>
                                            </Setter.Value>
                                        </Setter>
                                        <Trigger.Value>
                                            <s:Boolean>True</s:Boolean>
                                        </Trigger.Value>
                                    </Trigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ToggleButton.Style>
        </ToggleButton>
        <Border
            BorderThickness="{TemplateBinding Border.BorderThickness}"
            Padding="{TemplateBinding Control.Padding}"
            BorderBrush="{TemplateBinding Border.BorderBrush}"
            Background="{TemplateBinding Panel.Background}"
            Name="Bd"
            SnapsToDevicePixels="True"
            Grid.Column="1">
            <ContentPresenter
                Content="{TemplateBinding HeaderedContentControl.Header}"
                ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
                ContentStringFormat="{TemplateBinding HeaderedItemsControl.HeaderStringFormat}"
                ContentSource="Header"
                Name="PART_Header"
                HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
                SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
        </Border>
        <ItemsPresenter
            Name="ItemsHost"
            Grid.Column="1"
            Grid.Row="1"
            Grid.ColumnSpan="2" />
    </Grid>
    <ControlTemplate.Triggers>
        <Trigger
            Property="TreeViewItem.IsExpanded">
            <Setter
                Property="UIElement.Visibility"
                TargetName="ItemsHost">
                <Setter.Value>
                    <x:Static
                        Member="Visibility.Collapsed" />
                </Setter.Value>
            </Setter>
            <Trigger.Value>
                <s:Boolean>False</s:Boolean>
            </Trigger.Value>
        </Trigger>
        <Trigger
            Property="ItemsControl.HasItems">
            <Setter
                Property="UIElement.Visibility"
                TargetName="Expander">
                <Setter.Value>
                    <x:Static
                        Member="Visibility.Hidden" />
                </Setter.Value>
            </Setter>
            <Trigger.Value>
                <s:Boolean>False</s:Boolean>
            </Trigger.Value>
        </Trigger>
        <Trigger
            Property="TreeViewItem.IsSelected">
            <Setter
                Property="Panel.Background"
                TargetName="Bd">
                <Setter.Value>
                    <DynamicResource
                        ResourceKey="{x:Static SystemColors.HighlightBrushKey}" />
                </Setter.Value>
            </Setter>
            <Setter
                Property="TextElement.Foreground">
                <Setter.Value>
                    <DynamicResource
                        ResourceKey="{x:Static SystemColors.HighlightTextBrushKey}" />
                </Setter.Value>
            </Setter>
            <Trigger.Value>
                <s:Boolean>True</s:Boolean>
            </Trigger.Value>
        </Trigger>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition
                    Property="TreeViewItem.IsSelected">
                    <Condition.Value>
                        <s:Boolean>True</s:Boolean>
                    </Condition.Value>
                </Condition>
                <Condition
                    Property="Selector.IsSelectionActive">
                    <Condition.Value>
                        <s:Boolean>False</s:Boolean>
                    </Condition.Value>
                </Condition>
            </MultiTrigger.Conditions>
            <Setter
                Property="Panel.Background"
                TargetName="Bd">
                <Setter.Value>
                    <DynamicResource
                        ResourceKey="{x:Static SystemColors.ControlBrushKey}" />
                </Setter.Value>
            </Setter>
            <Setter
                Property="TextElement.Foreground">
                <Setter.Value>
                    <DynamicResource
                        ResourceKey="{x:Static SystemColors.ControlTextBrushKey}" />
                </Setter.Value>
            </Setter>
        </MultiTrigger>
        <Trigger
            Property="UIElement.IsEnabled">
            <Setter
                Property="TextElement.Foreground">
                <Setter.Value>
                    <DynamicResource
                        ResourceKey="{x:Static SystemColors.GrayTextBrushKey}" />
                </Setter.Value>
            </Setter>
            <Trigger.Value>
                <s:Boolean>False</s:Boolean>
            </Trigger.Value>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

use AllowDrop property and Drop event for TreeView itself.

this is from msdn: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/4b8bafe5-ae3e-439d-953a-f534a60dbb2d/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM