using System;
|
using System.Collections;
|
using System.Collections.ObjectModel;
|
using System.Collections.Specialized;
|
using System.ComponentModel;
|
using System.Data.Common;
|
using System.Globalization;
|
using System.Windows;
|
using System.Windows.Controls;
|
using System.Windows.Data;
|
using System.Windows.Media;
|
|
namespace UILib
|
{
|
public class TreeViewColumnWidthHelper
|
{
|
public static GridLength? GetAutoWidth(GridViewColumn column)
|
{
|
return (GridLength?)column.GetValue(AutoWidthProperty);
|
}
|
|
public static void SetAutoWidth(GridViewColumn column, GridLength? length)
|
{
|
column.SetValue(AutoWidthProperty, length);
|
}
|
|
public static readonly DependencyProperty AutoWidthProperty =
|
DependencyProperty.RegisterAttached("AutoWidth", typeof(GridLength?), typeof(GridViewColumn),
|
new PropertyMetadata(null));
|
}
|
|
public class SimpleTreeListViewButtonConverter : IMultiValueConverter
|
{
|
public static SimpleTreeListViewButtonConverter Instance = new SimpleTreeListViewButtonConverter();
|
public static readonly double sqrt2 = Math.Sqrt(2);
|
|
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
{
|
if (values[0] is TreeListViewItem tlvi)
|
{
|
if (values[1] is int level && parameter.ToString() == "Size")
|
{
|
double BaseSize = (double)values[2];
|
return (level + 1) * BaseSize;
|
}
|
|
|
if (values[1] is int level1 && values[2] is int index
|
&& values[3] is bool IsExpanded
|
&& values[4] is bool IsLastNode
|
&& values[5] is bool HasItems && parameter.ToString() == "Content")
|
{
|
double BaseSize = (double)values[6];
|
double BaseHalfSize = BaseSize / 2;
|
double Length = BaseHalfSize + 1;
|
|
var res = new GeometryGroup()
|
{
|
FillRule = FillRule.Nonzero
|
};
|
if (level1 == 0)
|
{
|
// 画上面那根线
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - (Length / 2)),
|
});
|
// 画右边那根线
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize + (Length / 2), BaseHalfSize),
|
EndPoint = new Point((level1 * BaseSize) + BaseSize, BaseHalfSize),
|
});
|
}
|
else
|
{
|
// 画上面那根线
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - (Length / 2)),
|
});
|
// 画右边那根线
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize + (Length / 2), BaseHalfSize),
|
EndPoint = new Point((level1 * BaseSize) + BaseSize, BaseHalfSize),
|
});
|
|
var parent = tlvi.parent;
|
while (parent != null)
|
{
|
if (!parent.IsLastNode)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((parent.level * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((parent.level * BaseSize) + BaseHalfSize, BaseSize),
|
});
|
}
|
parent = parent.parent;
|
}
|
}
|
|
if (!IsLastNode)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize + (Length / 2)),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseSize),
|
});
|
}
|
|
|
if (HasItems)
|
{
|
//画方
|
res.Children.Add(new RectangleGeometry(new Rect((level1 * BaseSize) + ((BaseSize - Length) / 2), ((BaseSize - Length) / 2), Length, Length)));
|
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize - (Length / 2),
|
BaseHalfSize),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize + (Length / 2), BaseHalfSize),
|
});
|
|
if (!IsExpanded)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize,
|
BaseHalfSize - (Length / 2)),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize,
|
BaseHalfSize + (Length / 2)),
|
});
|
}
|
}
|
else
|
{
|
if (!IsLastNode)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - (Length / 2)),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize + (Length / 2)),
|
});
|
}
|
else
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - (Length / 2)),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize),
|
});
|
}
|
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize + (Length / 2), BaseHalfSize),
|
});
|
}
|
tlvi.root.UpdateColumnsWidth();
|
return res;
|
}
|
}
|
return null;
|
}
|
|
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
{
|
throw new NotImplementedException();
|
}
|
}
|
|
public class TreeListViewButtonConverter : IMultiValueConverter
|
{
|
public static TreeListViewButtonConverter Instance = new TreeListViewButtonConverter();
|
|
|
public static readonly double sqrt2 = Math.Sqrt(2);
|
|
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
{
|
if (values[0] is TreeListViewItem tlvi)
|
{
|
if (values[1] is int level && parameter.ToString() == "Size")
|
{
|
double BaseSize = (double)values[2];
|
return (level + 1) * BaseSize;
|
}
|
|
if (parameter.ToString() == "Index")
|
{
|
|
int totalIndex = 1;
|
int Traverse(TreeListViewItem item)
|
{
|
if (item != tlvi)
|
{
|
totalIndex++;
|
foreach (var obj in item.Items)
|
{
|
var childItem = item.ItemContainerGenerator.ContainerFromItem(obj) as TreeListViewItem;
|
if (childItem != null)
|
return Traverse(childItem);
|
}
|
}
|
return totalIndex;
|
}
|
|
foreach (var obj in tlvi.root.Items)
|
{
|
var item_0 = tlvi.root.ItemContainerGenerator.ContainerFromItem(obj) as TreeListViewItem;
|
if (item_0 != null)
|
return Traverse(item_0);
|
}
|
return totalIndex;
|
}
|
|
|
if (values[1] is int level1 && values[2] is int index
|
&& values[3] is bool IsExpanded
|
&& values[4] is bool IsLastNode
|
&& values[5] is bool HasItems && parameter.ToString() == "Content")
|
{
|
double BaseSize = (double)values[6];
|
double BaseHalfSize = BaseSize / 2;
|
double Radius = BaseHalfSize *0.6;
|
var res = new GeometryGroup()
|
{
|
FillRule = FillRule.Nonzero
|
};
|
if (level1 == 0)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - Radius),
|
});
|
}
|
else
|
{
|
if (index == 0)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize), 0),
|
EndPoint = new Point((level1 * BaseSize) + (((sqrt2 * BaseHalfSize) - Radius) / sqrt2),
|
((sqrt2 * BaseHalfSize) - Radius) / sqrt2)
|
});
|
}
|
else
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize - Radius),
|
});
|
}
|
var parent = tlvi.parent;
|
while (parent != null)
|
{
|
if (!parent.IsLastNode)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((parent.level * BaseSize) + BaseHalfSize, 0),
|
EndPoint = new Point((parent.level * BaseSize) + BaseHalfSize, BaseSize),
|
});
|
}
|
parent = parent.parent;
|
}
|
}
|
|
if (!IsLastNode)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize + Radius),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize, BaseSize),
|
});
|
}
|
|
// 有子项并且是展开状态,统一画斜线
|
if (HasItems)
|
{
|
if (IsExpanded)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize + (((sqrt2 * BaseHalfSize) - Radius) / sqrt2),
|
BaseHalfSize + (((sqrt2 * BaseHalfSize) - Radius) / sqrt2)),
|
EndPoint = new Point((level1 + 1) * BaseSize, BaseSize),
|
});
|
}
|
}
|
|
//最后画圆
|
res.Children.Add(new EllipseGeometry
|
{
|
Center = new Point((level1 * BaseSize) + BaseHalfSize, BaseHalfSize),
|
RadiusX = Radius,
|
RadiusY = Radius,
|
|
});
|
|
if (HasItems)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize - Radius,
|
BaseHalfSize),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize + Radius, BaseHalfSize),
|
});
|
|
if (!IsExpanded)
|
{
|
res.Children.Add(new LineGeometry
|
{
|
StartPoint = new Point((level1 * BaseSize) + BaseHalfSize,
|
BaseHalfSize - Radius),
|
EndPoint = new Point((level1 * BaseSize) + BaseHalfSize,
|
BaseHalfSize + Radius),
|
});
|
}
|
}
|
tlvi.root.UpdateColumnsWidth();
|
return res;
|
}
|
}
|
return null;
|
}
|
|
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
{
|
throw new NotImplementedException();
|
}
|
}
|
|
public class TreeListView : TreeView
|
{
|
public static readonly DependencyProperty ColumnsProperty =
|
DependencyProperty.Register("Columns", typeof(GridViewColumnCollection), typeof(TreeListView),
|
new PropertyMetadata(null));
|
|
public GridViewColumnCollection Columns
|
{
|
get { return (GridViewColumnCollection)GetValue(ColumnsProperty); }
|
set { SetValue(ColumnsProperty, value); }
|
}
|
|
public static readonly DependencyProperty ColumnHeaderContainerStyleProperty =
|
DependencyProperty.Register("ColumnHeaderContainerStyle", typeof(Style), typeof(TreeListView),
|
new PropertyMetadata(null));
|
|
public Style ColumnHeaderContainerStyle
|
{
|
get { return (Style)GetValue(ColumnHeaderContainerStyleProperty); }
|
set { SetValue(ColumnHeaderContainerStyleProperty, value); }
|
}
|
|
static TreeListView()
|
{
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeListView), new FrameworkPropertyMetadata(typeof(TreeListView)));
|
}
|
|
public TreeListView()
|
{
|
Columns = new GridViewColumnCollection();
|
}
|
|
internal void UpdateColumnsWidth()
|
{
|
int i = 0;
|
// foreach(var item in this.It)
|
|
foreach (GridViewColumn col in Columns)
|
{
|
var length = TreeViewColumnWidthHelper.GetAutoWidth(col);
|
if (length == null)
|
{
|
continue;
|
}
|
if (length.Value.GridUnitType == GridUnitType.Auto)
|
{
|
if (double.IsNaN(col.Width))
|
{
|
col.Width = col.ActualWidth;
|
}
|
col.Width = double.NaN;
|
}
|
}
|
}
|
|
public override void OnApplyTemplate()
|
{
|
base.OnApplyTemplate();
|
}
|
|
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
|
{
|
base.OnItemsChanged(e);
|
}
|
|
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
{
|
base.OnItemsSourceChanged(oldValue, newValue);
|
if (oldValue is INotifyCollectionChanged oncc)
|
{
|
oncc.CollectionChanged -= Nncc_CollectionChanged;
|
}
|
|
if (oldValue is INotifyPropertyChanged onpc)
|
{
|
onpc.PropertyChanged -= Nnpc_PropertyChanged;
|
}
|
|
|
if (newValue is INotifyCollectionChanged nncc)
|
{
|
nncc.CollectionChanged += Nncc_CollectionChanged;
|
}
|
|
if (newValue is INotifyPropertyChanged nnpc)
|
{
|
nnpc.PropertyChanged += Nnpc_PropertyChanged;
|
}
|
}
|
|
internal void UpdateChildrenUI()
|
{
|
if (ItemsSource is IEnumerable source)
|
{
|
foreach (var obj in source)
|
{
|
var sb = this.ItemContainerGenerator.ContainerFromItem(obj) as TreeListViewItem;
|
sb?.TryUpdateUI();
|
}
|
}
|
}
|
|
private void Nnpc_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
{
|
//UpdateChildrenUI();
|
}
|
|
private void Nncc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
{
|
UpdateChildrenUI();
|
}
|
|
protected override DependencyObject GetContainerForItemOverride()
|
{
|
return new TreeListViewItem(0, this, null);
|
}
|
}
|
|
public class TreeListViewItem : TreeViewItem, INotifyPropertyChanged
|
{
|
public static readonly DependencyProperty ColumnsProperty =
|
DependencyProperty.Register("Columns", typeof(GridViewColumnCollection), typeof(TreeListViewItem),
|
new PropertyMetadata(null));
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
private int _totalIndex;
|
public int TotalIndex
|
{
|
get => _totalIndex;
|
set
|
{
|
if (_totalIndex != value)
|
{
|
_totalIndex = value;
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TotalIndex)));
|
}
|
}
|
}
|
|
public int level
|
{
|
get
|
{
|
int i = 0;
|
var p = parent;
|
while (p != null)
|
{
|
i++;
|
p = p.parent;
|
}
|
return i;
|
}
|
}
|
|
public int index
|
{
|
get
|
{
|
return parent == null ? root.Items.IndexOf(this.DataContext) : parent.Items.IndexOf(this.DataContext);
|
}
|
}
|
|
public bool IsLastNode
|
{
|
get
|
{
|
return parent == null ? index == root.Items.Count - 1 : index == parent.Items.Count - 1;
|
}
|
}
|
|
public TreeListView root { get; private set; }
|
public TreeListViewItem parent { get; private set; }
|
|
public TreeListViewItem(int level, TreeListView root, TreeListViewItem parent)
|
{
|
this.IsExpanded = true;
|
this.root = root;
|
this.parent = parent;
|
this.Loaded += TreeListViewItem_Loaded;
|
}
|
|
private void TreeListViewItem_Loaded(object sender, RoutedEventArgs e)
|
{
|
|
}
|
|
protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
|
{
|
base.OnItemsSourceChanged(oldValue, newValue);
|
if (oldValue is INotifyCollectionChanged oncc)
|
{
|
oncc.CollectionChanged -= Nncc_CollectionChanged;
|
}
|
|
if (oldValue is INotifyPropertyChanged onpc)
|
{
|
onpc.PropertyChanged -= Nnpc_PropertyChanged;
|
}
|
|
|
if (newValue is INotifyCollectionChanged nncc)
|
{
|
nncc.CollectionChanged += Nncc_CollectionChanged;
|
}
|
|
if (newValue is INotifyPropertyChanged nnpc)
|
{
|
nnpc.PropertyChanged += Nnpc_PropertyChanged;
|
}
|
}
|
|
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
|
{
|
base.OnItemsChanged(e);
|
}
|
|
private void Nnpc_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
{
|
//root.UpdateChildrenUI();
|
}
|
|
private void Nncc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
{
|
//root.UpdateChildrenUI();
|
TryUpdateUI();
|
}
|
|
internal void TryUpdateUI()
|
{
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(level)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(index)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsLastNode)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(HasItems)));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsExpanded)));
|
if (ItemsSource is IEnumerable source)
|
{
|
foreach (var obj in source)
|
{
|
var sb = this.ItemContainerGenerator.ContainerFromItem(obj) as TreeListViewItem;
|
sb?.TryUpdateUI();
|
}
|
}
|
}
|
|
static TreeListViewItem()
|
{
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeListViewItem), new FrameworkPropertyMetadata(typeof(TreeListViewItem)));
|
}
|
|
protected override DependencyObject GetContainerForItemOverride()
|
{
|
return new TreeListViewItem(level + 1, root, this);
|
}
|
}
|
}
|