So I have pretty much followed every example I can find on how to set the drag/drop effect on a WPF control to None to show the circle with a line symbol, but to no avail.
Here is the code I have copied from various examples, modifying it to get it work:
This is the code behind for the Window:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace TreeViewWithCheckBoxes
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
FooViewModel root = this.tree.Items[0] as FooViewModel;
base.CommandBindings.Add(
new CommandBinding(
ApplicationCommands.Undo,
(sender, e) => // Execute
{
e.Handled = true;
root.IsChecked = false;
this.tree.Focus();
},
(sender, e) => // CanExecute
{
e.Handled = true;
e.CanExecute = (root.IsChecked != false);
}));
this.tree.Focus();
}
private void TreeViewItemDragEnter(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.None;
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>
((DependencyObject)e.OriginalSource);
if (treeViewItem == null)
{
return;
}
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
{
return;
}
if (dropTarget.Parent == folderViewModel.Parent)
e.Effects = e.AllowedEffects;
else
{
e.Effects = DragDropEffects.None;
}
}
}
private void TreeViewItemDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>
((DependencyObject)e.OriginalSource);
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
return;
if (dropTarget.Parent == folderViewModel.Parent)
{
var dropType = dropTarget.Name;
var dragType = folderViewModel.Name;
var parent = dropTarget.Parent;
if (parent != null)
{
var dropLocation = -1;
var dragLocation = -1;
for (int index = 0; index < parent.Children.Count; ++index)
{
if (parent.Children[index].Name == dropType) dropLocation = index;
if (parent.Children[index].Name == dragType) dragLocation = index;
if (dropLocation != -1 && dragLocation != -1)
break;
}
if (dropLocation != -1 && dragLocation != -1)
{
parent.Children[dropLocation] = folderViewModel;
parent.Children[dragLocation] = dropTarget;
}
}
}
}
}
private void TreeViewItemMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
var mousePos = e.GetPosition(null);
var diff = StartPoint - mousePos;
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
{
var treeView = sender as TreeView;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>((DependencyObject)e.OriginalSource);
if (treeView == null || treeViewItem == null)
return;
var folderViewModel = treeView.SelectedItem as FooViewModel;
if (folderViewModel == null)
return;
var dragData = new DataObject(folderViewModel);
DragDrop.DoDragDrop(treeViewItem, dragData, DragDropEffects.Move);
}
}
}
private void TreeViewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
StartPoint = e.GetPosition(null);
}
public Point StartPoint { get; set; }
private void TreeViewItemDragOver(object sender, DragEventArgs e)
{
var container = sender as FrameworkElement;
if (container == null)
{
return;
}
var scrollViewer = VisualTreeHelperUtils.GetFirstVisualChild<ScrollViewer>(container);
if (scrollViewer == null)
{
return;
}
double tolerance = 30;
double verticalPos = e.GetPosition(container).Y;
double offset = 20;
if (verticalPos < tolerance) // Top of visible list?
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - offset); //Scroll up.
}
else if (verticalPos > container.ActualHeight - tolerance) //Bottom of visible list?
{
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + offset); //Scroll down.
}
e.Effects = DragDropEffects.None;
var source = e.OriginalSource as DependencyObject;
if (source == null)
{
return;
}
if (e.Data.GetDataPresent(typeof(FooViewModel)))
{
var folderViewModel = e.Data.GetData(typeof(FooViewModel))
as FooViewModel;
var treeViewItem =
VisualTreeHelperUtils.FindParent<TreeViewItem>((DependencyObject)e.OriginalSource);
if (treeViewItem == null)
{
return;
}
var dropTarget = treeViewItem.Header as FooViewModel;
if (dropTarget == null || folderViewModel == null)
{
return;
}
if (dropTarget.Parent == folderViewModel.Parent)
e.Effects = e.AllowedEffects;
else
{
e.Effects = DragDropEffects.None;
}
}
}
}
}
And here is the XAML for it, showing that it has registered with the proper events:
<Window
x:Class="TreeViewWithCheckBoxes.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TreeViewWithCheckBoxes"
xmlns:dw="clr-namespace:DrWPF.Windows.Controls"
FontSize="13"
Title="TreeView with CheckBoxes"
Width="300" Height="300"
WindowStartupLocation="CenterScreen"
>
<Window.Resources>
<ResourceDictionary>
<!-- Load this specific theme because the Aero theme for CheckBox has issues. -->
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/PresentationFramework.Royale;V3.0.0.0;31bf3856ad364e35;component\themes\royale.normalcolor.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="TreeViewItemStyle" TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="IsSelected" Value="{Binding IsInitiallySelected, Mode=OneTime}" />
<Setter Property="KeyboardNavigation.AcceptsReturn" Value="True" />
<Setter Property="dw:VirtualToggleButton.IsVirtualToggleButton" Value="True" />
<Setter Property="dw:VirtualToggleButton.IsChecked" Value="{Binding IsChecked}" />
</Style>
<HierarchicalDataTemplate
x:Key="CheckBoxItemTemplate"
ItemsSource="{Binding Children, Mode=OneTime}"
>
<StackPanel Orientation="Horizontal">
<!-- These elements are bound to a FooViewModel object. -->
<CheckBox
Focusable="False"
IsChecked="{Binding IsChecked}"
VerticalAlignment="Center"
/>
<ContentPresenter
Content="{Binding Name, Mode=OneTime}"
Margin="2,0"
/>
</StackPanel>
</HierarchicalDataTemplate>
</ResourceDictionary>
</Window.Resources>
<Window.DataContext>
<ObjectDataProvider
MethodName="CreateFoos"
ObjectType="{x:Type local:FooViewModel}" />
</Window.DataContext>
<TabControl>
<TabItem Header="Original">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height=".2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button
Grid.Row="3"
Command="Undo"
Content="Generate Report"
HorizontalAlignment="Center"
Margin="0,2"
Padding="8,0"
/>
<TreeView
Grid.Row="0"
x:Name="tree"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding Mode=OneTime}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}"
AllowDrop="True"
PreviewMouseLeftButtonDown="TreeViewMouseLeftButtonDown"
PreviewMouseMove="TreeViewItemMouseMove"
DragEnter="TreeViewItemDragEnter"
DragOver="TreeViewItemDragOver"
Drop="TreeViewItemDrop"
/>
<ScrollViewer Grid.Row="2" IsEnabled="{Binding ElementName=tree, Path=SelectedItem.IsChecked}">
<TextBlock Text="Oh Hai"></TextBlock>
<ScrollViewer.Style>
<Style TargetType="ScrollViewer">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5"></Setter>
<Setter Property="VerticalScrollBarVisibility" Value="Hidden"></Setter>
</Trigger>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Opacity" Value="1"></Setter>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</ScrollViewer.Style>
</ScrollViewer>
</Grid>
</TabItem>
<TabItem Header="Modified">
<DockPanel>
<Button
DockPanel.Dock="Bottom"
Command="Undo"
Content="Uncheck All"
HorizontalAlignment="Center"
Margin="0,2"
Padding="8,0"
/>
<TreeView
x:Name="modified"
ItemContainerStyle="{StaticResource TreeViewItemStyle}"
ItemsSource="{Binding Mode=OneTime}"
ItemTemplate="{StaticResource CheckBoxItemTemplate}"
/>
</DockPanel>
</TabItem>
</TabControl>
</Window>
I have not attached any code for the helper methods... those all work fine, and the basic idea of scrolling up/down while dragging, and exchanging items within a block works all fine.
However, the silly icon refuses to change for me. :)
I even tried to set it to None at the very top of the handler, and only set it back to AllowedEffects IF the parents are the same.
Essentially, the icon should switch to the circle with the line through it (None) if dragging to a parent that is not the same...
I have even set breakpoints to ensure that it is going into the case of the parents not being the same and setting the effect to None. Somehow, something must be switching it back, but I've no idea what...
Apparently, you also have to set e.Handled = true within the handler after setting e.Effects. After doing this, it works perfectly.