mirror of
https://github.com/sourcegit-scm/sourcegit
synced 2025-05-25 22:25:00 +00:00
228 lines
7.9 KiB
C#
228 lines
7.9 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
|
|
namespace SourceGit.Views.Controls {
|
|
|
|
/// <summary>
|
|
/// 树
|
|
/// </summary>
|
|
public class Tree : TreeView {
|
|
public static readonly DependencyProperty MultiSelectionProperty = DependencyProperty.Register(
|
|
"MultiSelection",
|
|
typeof(bool),
|
|
typeof(Tree),
|
|
new PropertyMetadata(false));
|
|
|
|
public bool MultiSelection {
|
|
get { return (bool)GetValue(MultiSelectionProperty); }
|
|
set { SetValue(MultiSelectionProperty, value); }
|
|
}
|
|
|
|
public static readonly DependencyProperty IndentProperty = DependencyProperty.Register(
|
|
"Indent",
|
|
typeof(double),
|
|
typeof(TreeItem),
|
|
new PropertyMetadata(16.0));
|
|
|
|
public double Indent {
|
|
get { return (double)GetValue(IndentProperty); }
|
|
set { SetValue(IndentProperty, value); }
|
|
}
|
|
|
|
public static readonly RoutedEvent SelectionChangedEvent = EventManager.RegisterRoutedEvent(
|
|
"SelectionChanged",
|
|
RoutingStrategy.Bubble,
|
|
typeof(RoutedEventHandler),
|
|
typeof(Tree));
|
|
|
|
public event RoutedEventHandler SelectionChanged {
|
|
add { AddHandler(SelectionChangedEvent, value); }
|
|
remove { RemoveHandler(SelectionChangedEvent, value); }
|
|
}
|
|
|
|
public List<object> Selected {
|
|
get;
|
|
set;
|
|
}
|
|
|
|
public Tree() {
|
|
Selected = new List<object>();
|
|
PreviewMouseDown += OnPreviewMouseDown;
|
|
}
|
|
|
|
public TreeItem FindItem(DependencyObject elem) {
|
|
if (elem == null) return null;
|
|
if (elem is TreeItem) return elem as TreeItem;
|
|
if (elem is Tree) return null;
|
|
return FindItem(VisualTreeHelper.GetParent(elem));
|
|
}
|
|
|
|
public void SelectAll() {
|
|
SelectAllChildren(this);
|
|
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
|
}
|
|
|
|
public void UnselectAll() {
|
|
if (Selected.Count == 0) return;
|
|
|
|
UnselectAllChildren(this);
|
|
Selected.Clear();
|
|
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
|
}
|
|
|
|
protected override DependencyObject GetContainerForItemOverride() {
|
|
return new TreeItem(0, Indent);
|
|
}
|
|
|
|
protected override bool IsItemItsOwnContainerOverride(object item) {
|
|
return item is TreeItem;
|
|
}
|
|
|
|
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) {
|
|
base.OnItemsSourceChanged(oldValue, newValue);
|
|
|
|
if (Selected.Count > 0) {
|
|
Selected.Clear();
|
|
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
|
}
|
|
}
|
|
|
|
private TreeItem FindItemByDataContext(ItemsControl control, object data) {
|
|
if (control == null) return null;
|
|
|
|
for (int i = 0; i < control.Items.Count; i++) {
|
|
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
|
if (control.Items[i] == data) return child;
|
|
|
|
var found = FindItemByDataContext(child, data);
|
|
if (found != null) return found;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void OnPreviewMouseDown(object sender, MouseButtonEventArgs e) {
|
|
var hit = VisualTreeHelper.HitTest(this, e.GetPosition(this));
|
|
if (hit == null || hit.VisualHit == null) return;
|
|
|
|
var item = FindItem(hit.VisualHit);
|
|
if (item == null) return;
|
|
|
|
if (!MultiSelection) {
|
|
if (item.IsChecked) return;
|
|
AddSelected(item, true);
|
|
return;
|
|
}
|
|
|
|
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) {
|
|
if (item.IsChecked) {
|
|
RemoveSelected(item);
|
|
} else {
|
|
AddSelected(item, false);
|
|
}
|
|
} else if ((Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) && Selected.Count > 0) {
|
|
var last = FindItemByDataContext(this, Selected.Last());
|
|
if (last == item) return;
|
|
|
|
var lastPos = last.PointToScreen(new Point(0, 0));
|
|
var curPos = item.PointToScreen(new Point(0, 0));
|
|
if (lastPos.Y > curPos.Y) {
|
|
SelectRange(this, item, last);
|
|
} else {
|
|
SelectRange(this, last, item);
|
|
}
|
|
|
|
AddSelected(item, false);
|
|
} else if (e.RightButton == MouseButtonState.Pressed) {
|
|
if (item.IsChecked) return;
|
|
AddSelected(item, true);
|
|
} else {
|
|
if (item.IsChecked && Selected.Count == 1) return;
|
|
AddSelected(item, true);
|
|
}
|
|
}
|
|
|
|
private void AddSelected(TreeItem item, bool removeOthers) {
|
|
if (removeOthers && Selected.Count > 0) {
|
|
UnselectAllChildren(this);
|
|
Selected.Clear();
|
|
}
|
|
|
|
item.IsChecked = true;
|
|
Selected.Add(item.DataContext);
|
|
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
|
}
|
|
|
|
private void RemoveSelected(TreeItem item) {
|
|
item.IsChecked = false;
|
|
Selected.Remove(item.DataContext);
|
|
RaiseEvent(new RoutedEventArgs(SelectionChangedEvent));
|
|
}
|
|
|
|
private void SelectAllChildren(ItemsControl control) {
|
|
for (int i = 0; i < control.Items.Count; i++) {
|
|
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
|
if (child == null) continue;
|
|
|
|
child.IsChecked = true;
|
|
Selected.Add(control.Items[i]);
|
|
SelectAllChildren(child);
|
|
}
|
|
}
|
|
|
|
private void UnselectAllChildren(ItemsControl control) {
|
|
for (int i = 0; i < control.Items.Count; i++) {
|
|
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
|
if (child == null) continue;
|
|
if (child.IsChecked) child.IsChecked = false;
|
|
UnselectAllChildren(child);
|
|
}
|
|
}
|
|
|
|
private int SelectRange(ItemsControl control, TreeItem from, TreeItem to, int matches = 0) {
|
|
for (int i = 0; i < control.Items.Count; i++) {
|
|
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
|
if (child == null) continue;
|
|
|
|
if (matches == 1) {
|
|
if (child == to) return 2;
|
|
Selected.Add(control.Items[i]);
|
|
child.IsChecked = true;
|
|
if (TryEndRangeSelection(child, to)) return 2;
|
|
} else if (child == from) {
|
|
matches = 1;
|
|
if (TryEndRangeSelection(child, to)) return 2;
|
|
} else {
|
|
matches = SelectRange(child, from, to, matches);
|
|
if (matches == 2) return 2;
|
|
}
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
private bool TryEndRangeSelection(ItemsControl control, TreeItem end) {
|
|
for (int i = 0; i < control.Items.Count; i++) {
|
|
var child = control.ItemContainerGenerator.ContainerFromIndex(i) as TreeItem;
|
|
if (child == null) continue;
|
|
|
|
if (child == end) {
|
|
return true;
|
|
} else {
|
|
Selected.Add(control.Items[i]);
|
|
child.IsChecked = true;
|
|
|
|
var ended = TryEndRangeSelection(child, end);
|
|
if (ended) return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
}
|