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

人盡茶涼 提交于 2019-12-12 04:17:47

问题


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.


回答1:


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

<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>



回答2:


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/



来源:https://stackoverflow.com/questions/5740655/dragging-and-dropping-treeviewitems-in-non-clickable-area-of-treeview-wpf

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!