You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
327 lines
7.2 KiB
327 lines
7.2 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Text; |
|
using System.Windows; |
|
using System.Windows.Controls; |
|
using System.Windows.Data; |
|
using System.Windows.Documents; |
|
using System.Windows.Input; |
|
using System.Windows.Media; |
|
using System.Windows.Media.Imaging; |
|
using System.Windows.Navigation; |
|
using System.Windows.Shapes; |
|
using System.Collections.Specialized; |
|
using ICSharpCode.XamlDesigner.Converters; |
|
using System.Collections; |
|
|
|
namespace ICSharpCode.XamlDesigner |
|
{ |
|
// limitations: |
|
// - Do not use ItemsSource (use Root) |
|
// - Do not use Items (use Root) |
|
public class DragTreeView : TreeView |
|
{ |
|
static DragTreeView() |
|
{ |
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragTreeView), |
|
new FrameworkPropertyMetadata(typeof(DragTreeView))); |
|
} |
|
|
|
public DragTreeView() |
|
{ |
|
AllowDrop = true; |
|
new DragListener(this).DragStarted += new MouseButtonEventHandler(DragTreeView_DragStarted); |
|
} |
|
|
|
DragTreeViewItem dropTarget; |
|
DragTreeViewItem treeItem; |
|
DragTreeViewItem dropAfter; |
|
int part; |
|
bool dropInside; |
|
bool dropCopy; |
|
bool canDrop; |
|
|
|
Border insertLine; |
|
|
|
public static readonly DependencyProperty RootProperty = |
|
DependencyProperty.Register("Root", typeof(object), typeof(DragTreeView)); |
|
|
|
public object Root { |
|
get { return (object)GetValue(RootProperty); } |
|
set { SetValue(RootProperty, value); } |
|
} |
|
|
|
//public object[] SelectedItems |
|
//{ |
|
// get { return Selection.Select(item => item.DataContext).ToArray(); } |
|
//} |
|
|
|
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) |
|
{ |
|
base.OnPropertyChanged(e); |
|
if (e.Property == RootProperty) { |
|
ItemsSource = new[] { Root }; |
|
} |
|
} |
|
|
|
void DragTreeView_DragStarted(object sender, MouseButtonEventArgs e) |
|
{ |
|
DragDrop.DoDragDrop(this, this, DragDropEffects.All); |
|
} |
|
|
|
public override void OnApplyTemplate() |
|
{ |
|
base.OnApplyTemplate(); |
|
insertLine = (Border)Template.FindName("PART_InsertLine", this); |
|
} |
|
|
|
protected override DependencyObject GetContainerForItemOverride() |
|
{ |
|
return new DragTreeViewItem(); |
|
} |
|
|
|
protected override bool IsItemItsOwnContainerOverride(object item) |
|
{ |
|
return item is DragTreeViewItem; |
|
} |
|
|
|
protected override void OnDragEnter(DragEventArgs e) |
|
{ |
|
ProcessDrag(e); |
|
} |
|
|
|
protected override void OnDragOver(DragEventArgs e) |
|
{ |
|
ProcessDrag(e); |
|
} |
|
|
|
protected override void OnDrop(DragEventArgs e) |
|
{ |
|
ProcessDrop(e); |
|
} |
|
|
|
protected override void OnDragLeave(DragEventArgs e) |
|
{ |
|
HideDropMarker(); |
|
} |
|
|
|
void PrepareDropInfo(DragEventArgs e) |
|
{ |
|
dropTarget = null; |
|
dropAfter = null; |
|
treeItem = (e.OriginalSource as DependencyObject).FindAncestor<DragTreeViewItem>(); |
|
|
|
if (treeItem != null) { |
|
var parent = ItemsControl.ItemsControlFromItemContainer(treeItem) as DragTreeViewItem; |
|
ContentPresenter header = treeItem.HeaderPresenter; |
|
Point p = e.GetPosition(header); |
|
part = (int)(p.Y / (header.ActualHeight / 3)); |
|
dropCopy = Keyboard.IsKeyDown(Key.LeftCtrl); |
|
dropInside = false; |
|
|
|
if (part == 1 || parent == null) { |
|
dropTarget = treeItem; |
|
dropInside = true; |
|
if (treeItem.Items.Count > 0) { |
|
dropAfter = treeItem.ItemContainerGenerator.ContainerFromIndex(treeItem.Items.Count - 1) as DragTreeViewItem; |
|
} |
|
} |
|
else if (part == 0) { |
|
dropTarget = parent; |
|
var index = dropTarget.ItemContainerGenerator.IndexFromContainer(treeItem); |
|
if (index > 0) { |
|
dropAfter = dropTarget.ItemContainerGenerator.ContainerFromIndex(index - 1) as DragTreeViewItem; |
|
} |
|
} |
|
else { |
|
dropTarget = parent; |
|
dropAfter = treeItem; |
|
} |
|
} |
|
} |
|
|
|
void ProcessDrag(DragEventArgs e) |
|
{ |
|
e.Effects = DragDropEffects.None; |
|
e.Handled = true; |
|
canDrop = false; |
|
|
|
if (e.Data.GetData(GetType()) != this) return; |
|
|
|
HideDropMarker(); |
|
PrepareDropInfo(e); |
|
|
|
if (dropTarget != null && CanInsertInternal()) { |
|
canDrop = true; |
|
e.Effects = dropCopy ? DragDropEffects.Copy : DragDropEffects.Move; |
|
DrawDropMarker(); |
|
} |
|
} |
|
|
|
void ProcessDrop(DragEventArgs e) |
|
{ |
|
HideDropMarker(); |
|
|
|
if (canDrop) { |
|
InsertInternal(); |
|
} |
|
} |
|
|
|
void DrawDropMarker() |
|
{ |
|
if (dropInside) { |
|
dropTarget.IsDragHover = true; |
|
} |
|
else { |
|
var header = treeItem.HeaderPresenter; |
|
var p = header.TransformToVisual(this).Transform( |
|
new Point(0, part == 0 ? 0 : header.ActualHeight)); |
|
|
|
insertLine.Visibility = Visibility.Visible; |
|
insertLine.Margin = new Thickness(p.X, p.Y, 0, 0); |
|
} |
|
} |
|
|
|
void HideDropMarker() |
|
{ |
|
insertLine.Visibility = Visibility.Collapsed; |
|
if (dropTarget != null) { |
|
dropTarget.IsDragHover = false; |
|
} |
|
} |
|
|
|
internal HashSet<DragTreeViewItem> Selection = new HashSet<DragTreeViewItem>(); |
|
DragTreeViewItem upSelection; |
|
|
|
internal void ItemMouseDown(DragTreeViewItem item) |
|
{ |
|
upSelection = null; |
|
bool control = Keyboard.IsKeyDown(Key.LeftCtrl); |
|
|
|
if (Selection.Contains(item)) { |
|
if (control) { |
|
Unselect(item); |
|
} |
|
else { |
|
upSelection = item; |
|
} |
|
} |
|
else { |
|
if (control) { |
|
Select(item); |
|
} |
|
else { |
|
SelectOnly(item); |
|
} |
|
} |
|
} |
|
|
|
internal void ItemMouseUp(DragTreeViewItem item) |
|
{ |
|
if (upSelection == item) { |
|
SelectOnly(item); |
|
} |
|
upSelection = null; |
|
} |
|
|
|
internal void ItemAttached(DragTreeViewItem item) |
|
{ |
|
if (item.IsSelected) Selection.Add(item); |
|
} |
|
|
|
internal void ItemDetached(DragTreeViewItem item) |
|
{ |
|
if (item.IsSelected) Selection.Remove(item); |
|
} |
|
|
|
internal void ItemIsSelectedChanged(DragTreeViewItem item) |
|
{ |
|
if (item.IsSelected) { |
|
Selection.Add(item); |
|
} |
|
else { |
|
Selection.Remove(item); |
|
} |
|
} |
|
|
|
void Select(DragTreeViewItem item) |
|
{ |
|
Selection.Add(item); |
|
item.IsSelected = true; |
|
OnSelectionChanged(); |
|
} |
|
|
|
void Unselect(DragTreeViewItem item) |
|
{ |
|
Selection.Remove(item); |
|
item.IsSelected = false; |
|
OnSelectionChanged(); |
|
} |
|
|
|
void SelectOnly(DragTreeViewItem item) |
|
{ |
|
ClearSelection(); |
|
Select(item); |
|
OnSelectionChanged(); |
|
} |
|
|
|
void ClearSelection() |
|
{ |
|
foreach (var treeItem in Selection.ToArray()) { |
|
treeItem.IsSelected = false; |
|
} |
|
Selection.Clear(); |
|
OnSelectionChanged(); |
|
} |
|
|
|
void OnSelectionChanged() |
|
{ |
|
} |
|
|
|
bool CanInsertInternal() |
|
{ |
|
if (!dropCopy) { |
|
var item = dropTarget; |
|
while (true) { |
|
if (Selection.Contains(item)) return false; |
|
item = ItemsControl.ItemsControlFromItemContainer(item) as DragTreeViewItem; |
|
if (item == null) break; |
|
} |
|
|
|
if (Selection.Contains(dropAfter)) return false; |
|
} |
|
|
|
return CanInsert(dropTarget, Selection.ToArray(), dropAfter, dropCopy); |
|
} |
|
|
|
void InsertInternal() |
|
{ |
|
var selection = Selection.ToArray(); |
|
|
|
if (!dropCopy) { |
|
foreach (var item in Selection.ToArray()) { |
|
var parent = ItemsControl.ItemsControlFromItemContainer(item) as DragTreeViewItem; |
|
//TODO |
|
if (parent != null) { |
|
Remove(parent, item); |
|
} |
|
} |
|
} |
|
Insert(dropTarget, selection, dropAfter, dropCopy); |
|
} |
|
|
|
protected virtual bool CanInsert(DragTreeViewItem target, DragTreeViewItem[] items, DragTreeViewItem after, bool copy) |
|
{ |
|
return true; |
|
} |
|
|
|
protected virtual void Insert(DragTreeViewItem target, DragTreeViewItem[] items, DragTreeViewItem after, bool copy) |
|
{ |
|
} |
|
|
|
protected virtual void Remove(DragTreeViewItem target, DragTreeViewItem item) |
|
{ |
|
} |
|
} |
|
}
|
|
|