我正在尝试让用户在TreeViewItems
附近移动TreeView
(具体来说,通过按住控件并按箭头键)。我可以将节点移入和移出其他节点,我可以在顶层上下移动节点,但是当我尝试在子节点内移动节点时,它什么都不做,如果我尝试在一个节点内移动节点子节点,我得到以下异常:
Element已经有一个逻辑父级。在将旧父级附加到新父级之前,必须将其与旧父级分离。
当我尝试将节点添加回其原始集合时(当然,在删除它之后)会发生这种情况。这是最初的实现:
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
必须与其逻辑父母分开,我从测试中知道TreeViewItem
的父母总是{ {1}},我只是不知道如何分离它。我知道你试图保持网站没有无聊的问题,我希望我的不是一个。任何见解都将不胜感激。
答案 0 :(得分:3)
QUICK答案是
[whatevertheparentofyourUIElement].Children.Remove(childrenitem);
CORRECT答案是:不要在代码中操纵UIElements。您有一个概念(设计)缺陷:您的UI不是数据,您的数据是数据,您的UI只是在屏幕上显示数据的好方法。如果你在ObservableCollection<T>
级别的某个地方ViewModel
并且不得不操纵它,那会不会容易得多?相反,你正在操纵复杂的WPF对象,执行大量不必要的意大利面条代码,并且由于UI虚拟化和类似的东西,你最终会遇到很多问题。
如果您以正确的方式实施了TreeView
,则此任务将减少为2行代码:从ObservableCollection
中删除所选项目并将其插入另一个ObservableCollection
位置分层ViewModel
结构中的其他位置。
答案 1 :(得分:0)
非常感谢您的输入,但经过进一步调试后,我意识到我的问题是getContainingList中的递归调用,
ItemCollection r = getContainingList((TreeViewItem)(test[i]), out id);
应该是
ItemCollection r = getContainingList(((TreeViewItem)(test[i])).Items, item, out id);
这就是我在凌晨2点编码的原因