using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
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 AlternatingRowConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is TreeViewItem item)
{
int globalIndex = GetGlobalIndex(item);
return globalIndex % 2 == 0 ? Brushes.White : new SolidColorBrush(Color.FromRgb(245, 245, 245));
}
return Brushes.White;
}
private int GetGlobalIndex(TreeViewItem item)
{
ItemsControl parent = ItemsControl.ItemsControlFromItemContainer(item);
if (parent == null) return 0;
// 获取当前item在父容器中的索引
int indexInParent;
if (parent is TreeViewItem treeViewItem)
{
indexInParent = GetGlobalIndex(treeViewItem);
}
else
{
indexInParent = 0;
}
// 父级内索引 * 2 + 当前索引
var parentItems = parent.Items;
int currentIndex = parentItems.IndexOf(item.DataContext);
return indexInParent * 2 + currentIndex;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotImplementedException();
}
///
/// 按钮样式转换
///
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();
}
}
///
/// StepList按钮转换
///
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 ChildrenPathProperty =
DependencyProperty.Register("ChildrenPath", typeof(string), typeof(TreeListView),
new PropertyMetadata(null));
public string ChildrenPath
{
get { return (string)GetValue(ChildrenPathProperty); }
set { SetValue(ChildrenPathProperty, value); }
}
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 BorderColorProperty =
DependencyProperty.Register("BorderColor", typeof(Brush), typeof(TreeListView),
new PropertyMetadata(Brushes.Transparent));
public Brush BorderColor
{
get { return (Brush)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, 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); }
}
///
/// 鼠标移动颜色
///
public static readonly DependencyProperty ItemHoverColorProperty =
DependencyProperty.Register("ItemHoverColor", typeof(Brush), typeof(TreeListView),
new PropertyMetadata(Brushes.Red));
public Brush ItemHoverColor
{
get { return (Brush)GetValue(ItemHoverColorProperty); }
set { SetValue(ItemHoverColorProperty, value); }
}
///
/// 鼠标移动颜色
///
public static readonly DependencyProperty ItemHoverForegroundProperty =
DependencyProperty.Register("ItemHoverForeground", typeof(Brush), typeof(TreeListView),
new PropertyMetadata(SystemColors.HighlightTextBrush));
public Brush ItemHoverForeground
{
get { return (Brush)GetValue(ItemHoverForegroundProperty); }
set { SetValue(ItemHoverForegroundProperty, value); }
}
///
/// Item选中颜色
///
public static readonly DependencyProperty ItemSelectedColorProperty =
DependencyProperty.Register("ItemSelected", typeof(Brush), typeof(TreeListView),
new PropertyMetadata(SystemColors.HighlightBrush));
public Brush ItemSelectedColor
{
get { return (Brush)GetValue(ItemSelectedColorProperty); }
set { SetValue(ItemSelectedColorProperty, value); }
}
///
/// Item选中字体颜色
///
public static readonly DependencyProperty ItemSelectedForegroundProperty =
DependencyProperty.Register("ItemSelectedForeground", typeof(Brush), typeof(TreeListView),
new PropertyMetadata(SystemColors.HighlightTextBrush));
public Brush ItemSelectedForeground
{
get { return (Brush)GetValue(ItemSelectedForegroundProperty); }
set { SetValue(ItemSelectedForegroundProperty, value); }
}
static TreeListView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TreeListView), new FrameworkPropertyMetadata(typeof(TreeListView)));
}
public TreeListView()
{
// this.SizeChanged += TreeListView_SizeChanged;
Columns = new GridViewColumnCollection();
}
private void TreeListView_SizeChanged(object sender, SizeChangedEventArgs e)
{
double oldWidth = e.PreviousSize.Width, newWidth = e.NewSize.Width;
double changed = newWidth - oldWidth;
int columns = 0;
if (oldWidth <= 0)
{
foreach (GridViewColumn col in Columns)
{
GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
if (length == null)
{
changed -= col.Width;
continue;
}
if (length.Value.GridUnitType == GridUnitType.Auto)
{
if (double.IsNaN(col.Width))
{
col.Width = col.ActualWidth;
}
col.Width = double.NaN;
changed -= col.ActualWidth;
}
else if (length.Value.GridUnitType == GridUnitType.Star)
{
columns += 1;
}
}
foreach (GridViewColumn col in Columns)
{
GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
if (length != null && length.Value.GridUnitType == GridUnitType.Star)
{
col.Width = Math.Max(20, col.Width + (changed / columns));
}
}
}
else
{
foreach (GridViewColumn col in Columns)
{
GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
if (length != null && length.Value.GridUnitType == GridUnitType.Star)
{
columns += 1;
}
}
if (columns <= 0)
{
return;
}
foreach (GridViewColumn col in Columns)
{
GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
if (length != null && length.Value.GridUnitType == GridUnitType.Star)
{
col.Width = Math.Max(20, col.Width + (changed / columns));
}
}
}
}
protected override void OnItemTemplateChanged(DataTemplate oldItemTemplate, DataTemplate newItemTemplate)
{
try
{
HierarchicalDataTemplate ttt = newItemTemplate as HierarchicalDataTemplate;
if (ttt != null)
{
ttt.ItemsSource = new Binding(ChildrenPath)
{
Mode = BindingMode.OneWay
};
}
}
catch
{
}
base.OnItemTemplateChanged(oldItemTemplate, newItemTemplate);
}
internal void UpdateColumnsWidth()
{
int i = 0;
var totalWidth = this.ActualWidth - 20;
int columns = 0;
// foreach(var item in this.It)
foreach (GridViewColumn col in Columns)
{
GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
if (length == null || length.Value.GridUnitType == GridUnitType.Pixel)
{
col.Width = col.Width;
totalWidth -= col.Width;
}
else if (length.Value.GridUnitType == GridUnitType.Auto)
{
if (double.IsNaN(col.Width))
{
col.Width = col.ActualWidth;
}
col.Width = double.NaN;
totalWidth -= col.ActualWidth;
}
else if (length.Value.GridUnitType == GridUnitType.Star)
{
columns += 1;
}
}
//foreach (GridViewColumn col in Columns)
//{
// GridLength? length = TreeViewColumnWidthHelper.GetAutoWidth(col);
// if (length != null && length.Value.GridUnitType == GridUnitType.Star)
// {
// col.Width = totalWidth / columns;
// }
//}
}
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 void TreeViewItem_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// 向上遍历视觉树,查找是否有需要处理双击的控件
DependencyObject current = e.OriginalSource as DependencyObject;
while (current != null)
{
// 如果找到需要处理双击的控件(例如Button、TextBox等),则不阻止事件
if (current is EditBox eb && eb.CanEdit)
{
return; // 让事件继续传递
}
current = VisualTreeHelper.GetParent(current);
}
e.Handled = true;
}
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;
this.AddHandler(TreeViewItem.RequestBringIntoViewEvent,
new RequestBringIntoViewEventHandler(TreeViewItem_RequestBringIntoView),
handledEventsToo: true);
}
private void TreeViewItem_RequestBringIntoView(object sender, RequestBringIntoViewEventArgs e)
{
// 取消自动滚动
e.Handled = true;
}
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);
}
}
}