У меня есть подкласс System.Windows.Forms.TreeView
, который вручную "привязан" к набору иерархических данных. Я хочу, чтобы пользователь мог редактировать метки дерева и отражать изменения обратно в данные. Поэтому я установил LabelEdit
на true и переопределил OnAfterLabelEdit
на мелодию:
protected override void OnAfterLabelEdit(NodeLabelEditEventArgs e)
{
base.OnAfterLabelEdit(e);
TreeNode node = e.Node;
if (PassesSomeValidation(e.Label))
{
MyDataNode dataNode = node.Tag as MyDataNode;
dataNode.SomeBoundValue = e.Label;
int oldIndex = node.Index;
int newIndex = RepositionChangedDataNode(dataNode);
TreeNode parent = node.Parent;
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}
else
{
e.CancelEdit = true;
}
}
RepositionChangedDataNode()
пересортирует данные и возвращает индекс, в который переместился узел изменения после сортировки. Я надеялся, что смогу просто переместить отредактированный узел, чтобы отразить это движение.
Проблема в том, что это заставляет узел оставаться в режиме редактирования ! Я пытался вызвать EndEdit()
, клонировать узел перед тем, как вставить его, установить LabelEdit
в false и вернуть в true, обернуть изменения в BeginUpdate()
/ EndUpdate()
и различные комбинации этих идей, но ни одна из них иметь какой-либо эффект.
Кажется, виновником является вставка. Даже если я попытаюсь вставить полностью новый узел, он немедленно перейдет в режим редактирования.
Итак, есть ли способ заставить TreeView
не вести себя таким образом? А если нет, то есть ли хороший обходной путь?
Некоторые идеи, которые я рассмотрел:
- Установить пользовательский TreeViewNodeSorter. Я бы предпочел не сортировать мои данные дважды.
- Установите флаг и задерживайте шаг удаления-вставки до некоторой точки после AfterLabelEdit. Это работает, чтобы сделать это во время WndProc, но это похоже на большой клудж, который может каким-то образом потерпеть неудачу.
Используйте BeginInvoke()
, чтобы перенести шаг удаления-вставки обратно в очередь сообщений следующим образом:
BeginInvoke(new MethodInvoker(delegate(
{
parent.Nodes.RemoveAt(oldIndex);
parent.Nodes.Insert(newIndex, node);
}));
Это работает и кажется мне чище, чем # 2, но я знаю, что, вероятно, это не то, как BeginInvoke()
предназначалось для использования, и что это может иметь последствия, которые не могут предсказать мои очень ограниченные знания о насосе сообщений.