在树视图中删除自下而上删除子项的最佳循环?

时间:2014-11-16 01:19:48

标签: c# wpf treeview

我将TreeView组织为:

Level1
   level 1.1
     level 1.1.1
     level 1.1.2
     level 1.1.3
       level 1.1.3.1
       level 1.1.3.2
   level 1.2
   level 1.3
level2
   level 2.1

 .............

每个级别都是一个继承自TreeViewModelBase的ViewModel。

给定一个类似于1.1.3.2的视图模型,此代码将从TreeView中删除它:

var y = SelectedItem as TreeViewModelBase;
var z = y.Parent;

z.Children.Remove(y);

if (z.Children.Count == 0)
{
    var g = z.Parent;
    g.Children.Remove(z);
} 

从所选项目开始并从父项中删除它的单个循环是什么。如果父项不再有子项,则从父项中删除父项,同样处理树结构。

执行此操作的最佳循环结构是什么?

感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

以下是我提出的建议:

  • 可以使用友好语法构建的项目模型(无需设置其父级)
  • 一个修剪工具,它将删除一个项目并尽可能地清理层次结构

实施例

(修剪项目1.4.1.1)

之前:

enter image description here

后:

enter image description here

物件:

Item:您的商品

  • 它会监视您添加到其中的子项,并且(un)设置其Parent属性
  • 提供Prune方法,将其从父级中移除,爬上并重复直到不可能

代码:

internal class Item
{
    [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ItemCollection _children;

    public Item()
    {
        Children = new ItemCollection();
    }

    public Item Parent { get; private set; }

    public ItemCollection Children
    {
        get { return _children; }
        set
        {
            if (_children != null)
            {
                _children.CollectionChanged -= Children_CollectionChanged;
            }

            if (value != null)
            {
                value.CollectionChanged += Children_CollectionChanged;
                // Notify about previously (never notified) added items 
                if (value.Count > 0)
                {
                    value.RaiseCollectionChanged(
                        new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
                }
            }

            _children = value;
        }
    }

    public string Name { get; set; }

    private void Children_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e != null)
        {
            if (e.NewItems != null)
            {
                IEnumerable<Item> newItems = e.NewItems.OfType<Item>();
                foreach (Item item in newItems)
                {
                    item.Parent = this;
                }
            }

            if (e.OldItems != null)
            {
                IEnumerable<Item> oldItems = e.OldItems.OfType<Item>();
                foreach (Item item in oldItems)
                {
                    item.Parent = null;
                }
            }
        }
    }

    public override string ToString()
    {
        return string.Format("{0}", Name);
    }

    public void Prune()
    {
        Item parent = Parent;
        if (parent != null)
        {
            parent.Children.Remove(this);


            while (parent.Children.Count <= 0)
            {
                Item grandParent = parent.Parent;
                if (grandParent != null)
                {
                    grandParent.Children.Remove(parent);
                    parent = grandParent;
                }
            }
        }
    }
}

ItemCollection

使用ObservableCollection的项目集合,非常有助于减少指定父级的需要,您将在下面的示例中看到。

internal class ItemCollection : ObservableCollection<Item>
{
    public ItemCollection()
    {
    }

    public ItemCollection(IEnumerable<Item> items)
    {
        foreach (Item item in items)
        {
            Add(item);
        }
    }

    internal void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        OnCollectionChanged(e);
    }
}

演示代码

public partial class MainWindow
{
    public MainWindow()
    {
        InitializeComponent();
        Loaded += MainWindow_Loaded;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        var root = new Item
        {
            Name = "root",
            Children = new ItemCollection(new[]
            {
                new Item
                {
                    Name = "item1.1",
                    Children = new ItemCollection(new[]
                    {
                        new Item {Name = "item1.1.1"},
                        new Item {Name = "item1.1.2"}
                    })
                },
                new Item
                {
                    Name = "item1.2",
                    Children = new ItemCollection(new[]
                    {
                        new Item {Name = "item1.2.1"}
                    })
                },
                new Item {Name = "item1.3"}
            })
        };

        var item1411 = new Item
        {
            Name = "item1.4.1.1"
        };
        var item141 = new Item
        {
            Name = "item1.4.1",
            Children = new ItemCollection(new[]
            {
                item1411
            })
        };
        var item14 = new Item
        {
            Name = "item1.4",
            Children = new ItemCollection(new[]
            {
                item141
            })
        };
        root.Children.Add(item14);

        Console.WriteLine("-----------------");
        Console.WriteLine("before pruning");
        Console.WriteLine("-----------------");
        PrintHierarchy(root);

        Console.WriteLine("-----------------");
        Console.WriteLine("after pruning");
        Console.WriteLine("-----------------");
        item1411.Prune();
        PrintHierarchy(root);

        DataContext = root;
    }

    private void PrintHierarchy(Item root, int level = 0)
    {
        Console.WriteLine("{0}{1}", string.Concat(Enumerable.Repeat(" ", level)), root);

        if (root.Children.Count > 0)
        {
            foreach (Item child in root.Children)
            {
                PrintHierarchy(child, level + 1);
            }
        }
    }
}

XAML:

<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfApplication3="clr-namespace:WpfApplication3"
        Title="MainWindow"
        Width="525"
        Height="350">
    <Grid>
        <Grid.Resources>
            <Style TargetType="TreeViewItem">
                <Setter Property="IsExpanded" Value="True" />
            </Style>
        </Grid.Resources>
        <TreeView ItemsSource="{Binding (wpfApplication3:Item.Children)}">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate DataType="wpfApplication3:Item" ItemsSource="{Binding Children}">
                    <HierarchicalDataTemplate.ItemTemplate>
                        <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                            <TextBlock Text="{Binding Name}" />
                        </HierarchicalDataTemplate>
                    </HierarchicalDataTemplate.ItemTemplate>
                    <TextBlock Text="{Binding Name}" />
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </Grid>
</Window>

修改

系统显然会反映树中的变化,例如:

private void Button_Click(object sender, RoutedEventArgs e)
{
    _item1411.Prune();
}

TreeView将无缝更新。

答案 1 :(得分:1)

我赞同艾贝的回答。在我的特定情况下,所有ViewModel都保存在ObservableCollections中,并且所有ViewModel都来自相同的TreeViewModelBase,下面的代码似乎更简单。 TreeView将自动更新。

  var z = SelectedItem as TreeViewModelBase;

   TreeViewModelBase y;
                        do
                        {
                            y = z;

                            if (y.Parent == null) break;
                            z = y.Parent;
                            z.Children.Remove(y);

                        } while(z.Children.Count==0);