private void FilterNodes()
        {
            var nodeAsync = nodeList.ToArray();

            // unfilter obsolete
            foreach (var node in nodeAsync
                     .Where(node => !FilteredItems.Contains(node.Title
                                                            .Replace("__", "_"))))
            {
                node.IsFiltered = false;
            }

            // filter new
            foreach (var node in nodeAsync
                     .Where(node => !node.IsFiltered && FilteredItems.Contains(node.Title.Replace("__", "_"))))
            {
                node.IsFiltered = true;
            }
        }
        private void OnDependencyPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            T item = (T)(LiveShapingItem <T>)dependencyObject;

            switch (GetCategoryOfDependencyProperty(args.Property))
            {
            case LiveShapingCategory.Sorting:
                int originalBinarySearchIndex = InternalBinarySearchWithEqualityCheck(item);
                int actualIndex;
                int targetIndex;

                if (originalBinarySearchIndex >= 0)
                {
                    Debug.Assert(BackingList[originalBinarySearchIndex].Equals(item));

                    actualIndex = originalBinarySearchIndex;
                    targetIndex = FindCorrectLocation(item);
                }
                else
                {
                    actualIndex = LinearSearch(item);
                    targetIndex = ~originalBinarySearchIndex;
                }

                LiveShapingItems[item].IsSortDirty = false;
                if (actualIndex >= 0)
                {
                    // adjust targetIndex if the item at actualIndex will no longer be there
                    if (actualIndex < targetIndex)
                    {
                        targetIndex--;
                    }
                    if (targetIndex != actualIndex)
                    {
                        BackingList.Move(actualIndex, targetIndex);
                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, item, targetIndex,
                                                                                 actualIndex));
                    }
                }
                break;

            case LiveShapingCategory.Filtering:
                var passesFilter = PassesFilter(item);

                if (passesFilter)
                {
                    if (FilteredItems.Remove(item))
                    {
                        var insertionIndex = base.Add(item);
                        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, insertionIndex));
                    }
                }
                else
                {
                    if (!FilteredItems.Contains(item))
                    {
                        var removalIndex = IndexOf(item);

                        // It is possible that the liveshapingitem has been registered but the item has not yet been added to this collection (causing index = -1), in which case this is a noop
                        if (removalIndex >= 0)
                        {
                            base.RemoveAt(removalIndex);
                            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, removalIndex));
                            FilteredItems.Add(item);
                        }
                    }
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            /* Debug.WriteLine(args.Property + "  " + string.Join(", ",
             *                   BackingList.OfType<GameFolderPair>()
             *                              .Select(
             *                                  folderPair => {
             *                                      if (folderPair.DestinationEntry == null) return "-99";
             *
             *                                      return SizeToStringConverter.Instance.Convert(folderPair.DestinationEntry?.Size)
             + (LiveShapingItems[folderPair as T].IsSortDirty ? "" : "✓");
             +                                  })
             +               ));*/
        }