162 lines
5.5 KiB
C#
162 lines
5.5 KiB
C#
using System.Collections.Specialized;
|
|
|
|
namespace JD.AssetBrowser.Model
|
|
{
|
|
/// <summary>
|
|
/// Provides a common implementation of a tree node model that supports three-state checking.
|
|
/// </summary>
|
|
public class ThreeStateCheckableTreeNodeModel : CheckableTreeNodeModel
|
|
{
|
|
|
|
private bool _isUpdatingIsChecked;
|
|
private ThreeStateCheckableTreeNodeModel _parent;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ThreeStateCheckableTreeNodeModel"/> class.
|
|
/// </summary>
|
|
public ThreeStateCheckableTreeNodeModel()
|
|
{
|
|
Children.CollectionChanged += OnChildrenCollectionChanged;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Occurs when the collection is changed.
|
|
/// </summary>
|
|
/// <param name="sender">The sender of the event.</param>
|
|
/// <param name="e">The <see cref="NotifyCollectionChangedEventArgs"/> containing data related to this event.</param>
|
|
private void OnChildrenCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (e != null)
|
|
{
|
|
if (e.OldItems != null)
|
|
{
|
|
foreach (var oldItem in e.OldItems)
|
|
{
|
|
if (oldItem is ThreeStateCheckableTreeNodeModel oldChild)
|
|
oldChild.Parent = null;
|
|
}
|
|
}
|
|
|
|
if (e.NewItems != null)
|
|
{
|
|
foreach (var newItem in e.NewItems)
|
|
{
|
|
if (newItem is ThreeStateCheckableTreeNodeModel newChild)
|
|
newChild.Parent = this;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies recursive updates based on the checked state.
|
|
/// </summary>
|
|
private void ApplyRecursiveUpdates()
|
|
{
|
|
if (!IsChecked.HasValue)
|
|
return;
|
|
|
|
foreach (var child in Children)
|
|
{
|
|
if (child is ThreeStateCheckableTreeNodeModel checkableChild)
|
|
ApplyRecursiveUpdates(checkableChild, IsChecked);
|
|
}
|
|
|
|
var ancestor = _parent;
|
|
while (ancestor != null)
|
|
{
|
|
var allChildrenChecked = true;
|
|
var allChildrenUnchecked = true;
|
|
|
|
foreach (var child in ancestor.Children)
|
|
{
|
|
if (child is ThreeStateCheckableTreeNodeModel checkableChild)
|
|
{
|
|
switch (checkableChild.IsChecked)
|
|
{
|
|
case true:
|
|
allChildrenUnchecked = false;
|
|
break;
|
|
case false:
|
|
allChildrenChecked = false;
|
|
break;
|
|
default:
|
|
allChildrenChecked = false;
|
|
allChildrenUnchecked = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (allChildrenChecked)
|
|
ancestor.SetIsCheckedWithoutRecursion(true);
|
|
else if (allChildrenUnchecked)
|
|
ancestor.SetIsCheckedWithoutRecursion(false);
|
|
else
|
|
ancestor.SetIsCheckedWithoutRecursion(null);
|
|
|
|
ancestor = ancestor.Parent;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies recursive updates based on the checked state.
|
|
/// </summary>
|
|
/// <param name="node">The node to update.</param>
|
|
/// <param name="newValue">The new value.</param>
|
|
private void ApplyRecursiveUpdates(ThreeStateCheckableTreeNodeModel node, bool? newValue)
|
|
{
|
|
if ((node != null) && (node.IsChecked != newValue))
|
|
{
|
|
node.SetIsCheckedWithoutRecursion(newValue);
|
|
|
|
foreach (var child in node.Children)
|
|
{
|
|
if (child is ThreeStateCheckableTreeNodeModel checkableChild)
|
|
ApplyRecursiveUpdates(checkableChild, newValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected override void OnPropertyChanged(string propertyName = null)
|
|
{
|
|
base.OnPropertyChanged(propertyName);
|
|
|
|
switch (propertyName)
|
|
{
|
|
case nameof(IsChecked):
|
|
if (!_isUpdatingIsChecked)
|
|
ApplyRecursiveUpdates();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the parent node, whose reference is needed for check updates that affect the ancestor nodes.
|
|
/// </summary>
|
|
/// <value>The parent node, whose reference is needed for check updates that affect the ancestor nodes.</value>
|
|
public ThreeStateCheckableTreeNodeModel Parent
|
|
{
|
|
get { return _parent; }
|
|
set { SetProperty(ref _parent, value); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the <c>IsChecked</c> property without recursion.
|
|
/// </summary>
|
|
/// <param name="newValue">The new value.</param>
|
|
public void SetIsCheckedWithoutRecursion(bool? newValue)
|
|
{
|
|
try
|
|
{
|
|
_isUpdatingIsChecked = true;
|
|
IsChecked = newValue;
|
|
}
|
|
finally
|
|
{
|
|
_isUpdatingIsChecked = false;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|