using System.ComponentModel; using System.Drawing; using System.Windows.Media; namespace System.Windows.Forms { public class TreeNode : INotifyPropertyChangedImpl { private TreeView owner; private string savedText; internal TreeView Owner => owner; public TreeNodeCollection Nodes { get; } public TreeNode Parent { get; private set; } public string Name { get; set; } private string text; public string Text { get => text; set => this.RaiseAndSetIfChanged(ref text, value); } private int imageIndex; public int ImageIndex { get => imageIndex; set { if (imageIndex != value) { imageIndex = value; ResetImageSource(); } } } public int SelectedImageIndex { get => ImageIndex; set => ImageIndex = value; } private ImageSource imageSource; public ImageSource ImageSource { get { if (imageSource == null) { var imageList = owner?.ImageList; if (imageList != null && imageIndex >= 0 && imageIndex < imageList.Images.Count) imageSource = imageList.Images.ImageSources[imageIndex]; } return imageSource; } } public int ImageSourceWidth => owner?.ImageList != null ? (int)(owner.ImageList.ImageSize.Width / owner.DpiScale) : 16; public int ImageSourceHeight => owner?.ImageList != null ? (int)(owner.ImageList.ImageSize.Height / owner.DpiScale) : 16; private bool? isChecked = false; [EditorBrowsable(EditorBrowsableState.Never)] public bool? IsChecked { get => isChecked; set { if (isChecked != value) { var args = new TreeViewCancelEventArgs(this, false, TreeViewAction.Unknown); owner?.DoBeforeCheck(args); if (!args.Cancel) { this.RaiseAndSetIfChanged(ref isChecked, value); var args1 = new TreeViewEventArgs(this); owner?.DoAfterCheck(args1); } } } } public bool Checked { get => IsChecked ?? false; set => IsChecked = value; } private bool isExpanded; public bool IsExpanded { get => isExpanded; set { if (isExpanded != value) { var args = new TreeViewCancelEventArgs(this, false, TreeViewAction.Unknown); if (isExpanded) owner?.DoBeforeCollapse(args); else owner?.DoBeforeExpand(args); if (!args.Cancel) { this.RaiseAndSetIfChanged(ref isExpanded, value); var args1 = new TreeViewEventArgs(this); if (isExpanded) owner?.DoAfterExpand(args1); else owner?.DoAfterCollapse(args1); } } } } private bool isSelected; public bool IsSelected { get => isSelected; set { if (isSelected != value) { if (value && owner != null) value = owner.DoNodeSelect(this); if (value) ExpandPath(); this.RaiseAndSetIfChanged(ref isSelected, value); } } } private bool isEditing; public bool IsEditing { get => isEditing; set => this.RaiseAndSetIfChanged(ref isEditing, value); } private System.Drawing.Color backColor; public System.Drawing.Color BackColor { get => backColor; set { if (backColor != value) { backColor = value; backColorBrush = null; RaisePropertyChanged("BackColorBrush"); } } } private System.Drawing.Color foreColor; public System.Drawing.Color ForeColor { get => foreColor; set { if (foreColor != value) { foreColor = value; foreColorBrush = null; RaisePropertyChanged("IsForeColorSet"); RaisePropertyChanged("ForeColorBrush"); } } } public System.Windows.Media.SolidColorBrush backColorBrush; [EditorBrowsable(EditorBrowsableState.Never)] public System.Windows.Media.SolidColorBrush BackColorBrush { get { if (backColorBrush == null) backColorBrush = new System.Windows.Media.SolidColorBrush(Helper.GetColor(BackColor)); return backColorBrush; } } public System.Windows.Media.SolidColorBrush foreColorBrush; [EditorBrowsable(EditorBrowsableState.Never)] public System.Windows.Media.SolidColorBrush ForeColorBrush { get { if (foreColorBrush == null) foreColorBrush = new System.Windows.Media.SolidColorBrush(Helper.GetColor(ForeColor)); return foreColorBrush; } } public string FullPath { get { if (owner == null) return null; var path = ""; var item = this; while (item != null) { path = item.Text + (path == "" ? "" : owner.PathSeparator) + path; item = item.Parent; } return path; } } public int Index => (Parent == null ? owner.Nodes : Parent.Nodes).IndexOf(this); public TreeNode NextNode { get { var nodes = Parent == null ? owner.Nodes : Parent.Nodes; var index = Index; if (index + 1 < nodes.Count) return nodes[index + 1]; return null; } } public TreeNode PrevNode { get { var nodes = Parent == null ? owner.Nodes : Parent.Nodes; var index = Index; if (index - 1 >= 0) return nodes[index - 1]; return null; } } public object Tag { get; set; } private Rectangle bounds; public Rectangle Bounds => bounds; // TODO: FIXME all use cases internal void SetBounds(Rectangle bounds) => this.bounds = bounds; private void ExpandPath() { var parent = Parent; while (parent != null) { parent.IsExpanded = true; parent = parent.Parent; } } internal void SetOwner(TreeView owner) { this.owner = owner; Nodes.SetOwner(owner); } internal void SetParent(TreeNode parent) => Parent = parent; internal void ResetImageSource() { imageSource = null; RaisePropertyChanged("ImageSource"); RaisePropertyChanged("ImageSourceWidth"); RaisePropertyChanged("ImageSourceHeight"); } internal void ResetImageSourceAll() { ResetImageSource(); for (int i = 0; i < Nodes.Count; i++) Nodes[i].ResetImageSourceAll(); } public void Expand() => IsExpanded = true; public void Collapse() => IsExpanded = false; public void ExpandAll() { Expand(); for (int i = 0; i < Nodes.Count; i++) Nodes[i].ExpandAll(); } public void CollapseAll() { Collapse(); for (int i = 0; i < Nodes.Count; i++) Nodes[i].CollapseAll(); } public void BeginEdit() { var args = new NodeLabelEditEventArgs(this); owner.DoOnBeforeLabelEdit(args); if (!args.CancelEdit) { savedText = Text; IsEditing = true; owner.SetEditedNode(this); } } public void EndEdit(bool cancel) { var args = new NodeLabelEditEventArgs(this, cancel ? null : Text); // in WinForms OnAfterLabelEdit the node.Text has old value Text = savedText; owner.DoOnAfterLabelEdit(args); if (args.CancelEdit) cancel = true; IsEditing = false; if (!cancel) Text = args.Label; owner.SetEditedNode(null); } public void Remove() { if (Parent != null) Parent.Nodes.Remove(this); else owner.Nodes.Remove(this); } public TreeNode() { Nodes = new(this); backColor = Drawing.SystemColors.Window; foreColor = Drawing.SystemColors.WindowText; } public TreeNode(string text) : this() { Text = text; } public TreeNode(string text, TreeNode[] nodes) : this(text) { foreach (var node in nodes) Nodes.Add(node); } } }