2

我试图让用户TreeViewItems在 a 周围移动TreeView(特别是通过按住控制键并按下箭头键)。我可以将节点移入和移出其他节点,并且可以在顶层上下移动节点,但是当我尝试在子节点内向上移动节点时,它什么也不做,如果我尝试在一个子节点内向下移动节点子节点,我得到以下异常:

元素已经有一个逻辑父级。在附加到新的父级之前,它必须与旧的父级分离。

当我尝试将节点添加回其原始集合时会发生这种情况(当然是在删除它之后)。这是原始实现:

private TreeViewItem getParent(TreeViewItem item)
{
    for (int i=0; i<fragment_tree.Items.Count; ++i)
    {
        TreeViewItem r = getParent((TreeViewItem)(fragment_tree.Items[i]), item);
        if (r != null)
        {
            return r;
        }
    }
    return null;
}
private TreeViewItem getParent(TreeViewItem test, TreeViewItem item)
{
    for (int i=0; i<test.Items.Count; ++i)
    {
        if (test.Items[i] == item)
        {
            return test;
        }
    }
    for (int i=0; i<test.Items.Count; ++i)
    {
        TreeViewItem r = getParent((TreeViewItem)(test.Items[i]), item);
        if (r != null)
        {
            return r;
        }
    }
    return null;
}
private ItemCollection getContainingList(TreeViewItem item, out int id)
{
    return getContainingList(fragment_tree.Items, item, out id);
}
private ItemCollection getContainingList(ItemCollection test, TreeViewItem item, out int id)
{
    for (int i=0; i<test.Count; ++i)
    {
        if (test[i] == item)
        {
            id = i;
            return test;
        }
    }
    for (int i=0; i<test.Count; ++i)
    {
        ItemCollection r = getContainingList((TreeViewItem)(test[i]), out id);
        if (r != null)
        {
            return r;
        }
    }
    id = -1;
    return null;
}
private void fragment_tree_PreviewKeyDown(object sender, KeyEventArgs e)
{
    TreeViewItem selected_item = (TreeViewItem)(fragment_tree.SelectedItem);
    if (selected_item.Header is String)
    {
        if (e.KeyboardDevice.IsKeyDown(Key.LeftCtrl) || e.KeyboardDevice.IsKeyDown(Key.RightCtrl))
        {
            if (e.Key == Key.Up)
            {
                int id;
                ItemCollection collection = getContainingList(selected_item, out id);
                if (collection != null) // it'll never be null, but w/e
                {
                    if (id > 0)
                    {
                        collection.RemoveAt(id);
                        collection.Insert(id-1, selected_item);
                        selected_item.IsSelected = true;
                    }
                }
                e.Handled = true;
            }
            else if (e.Key == Key.Down)
            {
                int id;
                ItemCollection collection = getContainingList(selected_item, out id);
                if (collection != null) // it'll never be null, but w/e
                {
                    if (id < collection.Count)
                    {
                        collection.RemoveAt(id);
                        collection.Insert(id+1, selected_item); // here is the exception
                        selected_item.IsSelected = true;
                    }
                }
                e.Handled = true;
            }
            else if (e.Key == Key.Left)
            {
                TreeViewItem parent = getParent(selected_item);
                if (parent != null)
                {
                    int id;
                    ItemCollection collection = getContainingList(parent, out id);
                    parent.Items.RemoveAt(id);
                    collection.Insert(id, selected_item);
                    selected_item.IsSelected = true;
                }
                e.Handled = true;
            }
            else if (e.Key == Key.Right)
            {
                int id;
                ItemCollection collection = getContainingList(selected_item, out id);
                if (id+1 < collection.Count)
                {
                    TreeViewItem next_item = (TreeViewItem)(collection[id+1]);
                    collection.RemoveAt(id);
                    next_item.Items.Insert(0, selected_item);
                    next_item.IsExpanded = true;
                    selected_item.IsSelected = true;
                }
                e.Handled = true;
            }
        }
    }

我尝试对所选内容进行深度克隆TreeViewItem(尽管我不想承担开销),但出现了一些奇怪的行为。当我尝试在它的子树中向上或向下移动一个项目时,它会跳到父级。当我尝试向上或向下移动顶层节点时,它会删除其邻居。我觉得好像缺少一些基本的东西

private TreeViewItem cloneTreeViewItem(TreeViewItem item)
{
    TreeViewItem r = new TreeViewItem();
    r.Header = item.Header;
    r.Tag = item.Tag;
    for (int i=0; i<item.Items.Count; ++i)
    {
        r.Items.Add(cloneTreeViewItem((TreeViewItem)(item.Items[i])));
    }
    return r;
}

....
if (e.Key == Key.Up)
{
    int id;
    ItemCollection collection = getContainingList(selected_item, out id);
    if (collection != null) // it'll never be null, but w/e
    {
        if (id > 0)
        {
            collection.RemoveAt(id);
            TreeViewItem clone = cloneTreeViewItem(selected_item);
            collection.Insert(id-1, clone);
            clone.IsSelected = true;
        }
    }
    e.Handled = true;
}
else if (e.Key == Key.Down)
{
    int id;
    ItemCollection collection = getContainingList(selected_item, out id);
    if (collection != null) // it'll never be null, but w/e
    {
        if (id < collection.Count)
        {
            collection.RemoveAt(id);
            TreeViewItem clone = cloneTreeViewItem(selected_item);
            collection.Insert(id+1, clone);
            clone.IsSelected = true;
        }
    }
    e.Handled = true;
}
.....

我确实花了至少一个小时研究这个主题,我知道TreeViewItem必须将其与其逻辑父级分离,并且我从测试中知道 a 的父级TreeViewItem始终是 a TreeView,我只是不知道如何分离它。我知道你试图让网站没有无聊的问题,我希望我的不是。任何见解将不胜感激。

4

2 回答 2

3

快速的答案是

[whatevertheparentofyourUIElement].Children.Remove(childrenitem);

正确的答案是:不要在代码中操作 UIElements。您有一个概念(设计)缺陷:您的 UI 不是数据,您的数据是数据,您的 UI 只是在屏幕上显示数据的好方法。ObservableCollection<T>如果你在关卡中有一个地方ViewModel并且必须操纵它,那不是更容易吗?相反,您正在操作复杂的 WPF 对象,编写大量不必要的意大利面条式代码,并且由于 UI 虚拟化之类的东西,您最终会遇到很多问题。

如果您TreeView以正确的方式实现,则此任务将减少为 2 行代码:从一个中删除所选项目ObservableCollection并将其插入ObservableCollection位于层次ViewModel结构中其他位置的另一个中。

于 2012-11-03T03:24:12.563 回答
0

好吧,感谢您的输入,但是经过进一步调试,我意识到我的问题是 getContainingList 中的递归调用,

ItemCollection r = getContainingList((TreeViewItem)(test[i]), out id);

应该

ItemCollection r = getContainingList(((TreeViewItem)(test[i])).Items, item, out id);

这就是我凌晨 2 点编码所得到的

于 2012-11-04T17:49:58.730 回答