何时在OnNewText事件后重绘VirtualTreeView?

3
我使用这段代码填充VirtualStringTree并允许重命名项目:
//---------------------------------------------------------------------------
// Structure for the tree
//---------------------------------------------------------------------------
struct TVSTdata
{
UnicodeString Name;
};
//---------------------------------------------------------------------------
// Initialization of the tree
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
VirtualStringTree1->NodeDataSize = sizeof(TVSTdata);
// Fill all nodes with initial data
InitializeTree();
}
//---------------------------------------------------------------------------
// Fill all nodes with data and assign FocusedNode
//---------------------------------------------------------------------------
void TForm1::InitializeTree()
{
TVirtualNode* pNode;
TVirtualNode* pActiveNode;
TVSTdata*     pData;

VirtualStringTree1->BeginUpdate();
VirtualStringTree1->Clear();

pNode = VirtualStringTree1->AddChild(NULL); pData = static_cast<TVSTdata*>(VirtualStringTree1->GetNodeData(pNode)); pData->Name = "This is name 1";
pNode = VirtualStringTree1->AddChild(NULL); pData = static_cast<TVSTdata*>(VirtualStringTree1->GetNodeData(pNode)); pData->Name = "This is name 2";
pNode = VirtualStringTree1->AddChild(NULL); pData = static_cast<TVSTdata*>(VirtualStringTree1->GetNodeData(pNode)); pData->Name = "This is name 3"; pActiveNode = pNode;
pNode = VirtualStringTree1->AddChild(NULL); pData = static_cast<TVSTdata*>(VirtualStringTree1->GetNodeData(pNode)); pData->Name = "This is name 4";
pNode = VirtualStringTree1->AddChild(NULL); pData = static_cast<TVSTdata*>(VirtualStringTree1->GetNodeData(pNode)); pData->Name = "This is name 5";

VirtualStringTree1->Selected[pActiveNode] = true;
VirtualStringTree1->FocusedNode = pActiveNode; // PROBLEM -> if assigned from within OnNewText will still remain NULL and won't be set to pActiveNode!
VirtualStringTree1->EndUpdate();
}
//---------------------------------------------------------------------------
// Just display the text
//---------------------------------------------------------------------------
void __fastcall TForm1::VirtualStringTree1GetText(TBaseVirtualTree *Sender, PVirtualNode Node, TColumnIndex Column, TVSTTextType TextType, UnicodeString &CellText)
{
TVSTdata* pData = static_cast<TVSTdata*>(Sender->GetNodeData(Node));
CellText = pData->Name;
}
//---------------------------------------------------------------------------
// Allow editing
//---------------------------------------------------------------------------
void __fastcall TForm1::VirtualStringTree1Editing(TBaseVirtualTree *Sender, PVirtualNode Node, TColumnIndex Column, bool &Allowed)
{
Allowed = true;
}
//---------------------------------------------------------------------------
// Now this is where ideally I would reload the tree with new data - after rename
//---------------------------------------------------------------------------
void __fastcall TForm1::VirtualStringTree1NewText(TBaseVirtualTree *Sender, PVirtualNode Node, TColumnIndex Column, UnicodeString NewText)
{
NewText = "not important for this example as tree is reloaded anyway";
InitializeTree();  // ERROR is here - after assigning FocusedNode it is still NULL
//Timer1->Enabled = true; // If delayed call FocusedNode is correctly assigned and not NULL
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
//InitializeTree();
//Timer1->Enabled = false;
}
//---------------------------------------------------------------------------

问题 - 当最初调用InitializeTree()并且VirtualStringTree1->FocusedNode被分配时,它被正确地分配(不为NULL)。
然而,如果在OnNewText中调用此InitializeTree()函数以便在重命名事件后从数据库重新加载树 - 在分配FocusedNode后,它仍然为空。因此,很明显树无法在OnNewText事件中重新加载并分配FocusedNode
我实现了一个延迟调用来重新加载新树并重新分配FocusedNode - 通过实现一个快速而简单的计时器(可以使用PostMessage进行延迟函数调用,但这只是一个愚蠢的例子) - 在计时器内部分配后,它不再为空并且按预期工作。
有人能指出实现树的重新加载的最佳方法吗 - 比如使用哪个特定事件来设置新的FocusedNode是安全的,并且不会被重新分配回NULL?延迟函数调用是实现此目的的唯一方法还是有更好的事件可以捕获(例如,如果在OnNewText之后发生某个事件,如果该事件不允许设置聚焦节点)。当然,这样可以工作,但我想知道是否有更好的方法来实现这一点。

2
如果你在谈论数据库,当你只编辑单个节点时,你真的需要重新加载整个树吗?难道你不想在编辑节点时更新特定行,并从数据库中仅重新加载该单个行(重新加载该节点)吗? - TLama
我需要重新排序树,因为编辑的节点可能会改变字母顺序。从已排序的数据库重新加载树比单独对树进行排序更容易。由于加载函数还会对树进行排序并创建多级树,因此以这种方式处理更容易。此外,在 VT 中进行的排序可能与数据库中的排序不同。另外,如果我只重新加载那个节点,我仍然需要确保该节点是焦点。 - Coder12345
1
从数据库重新加载排序后的数据可能比对树进行排序更容易,但这是过度设计。如果需要对树进行排序,只需在节点编辑后调用 SortTree 并处理 OnCompareNodes 事件。针对您的问题,首先您必须记住在清除树之前选择了哪个节点。但是,您不能通过存储 PVirtualNode 来实现这一点,因为当您清除并重新填充树时,您存储的节点指针将不再有效。您必须存储所选节点的索引。然后,在重新填充树后,通过存储的索引找到新节点并选择该节点。 - TLama
在数据库和树中有效地进行排序就好像有两个函数来完成同一件事情。但这是一个偏好问题。没有人说我正在存储 PVirtualNode - 我正在使用数据库索引来查找相关节点以便重新绘制。再次强调,问题不在于找到相关节点并选择它(上面的代码已经完成了所有这些工作),而是如果你在上面的代码中直接调用 VirtualStringTree1->FocusedNode = pActiveNode; 它将保持为 NULL,不会更改为 pActiveNode。请看上面问题的最后一段。 - Coder12345
1
我还应该指出,这是一棵小树,因此从数据库加载并重新排序是最快的方式,既不会对数据库产生巨大影响,也不会对树造成影响。而 BeginUpdateEndUpdate 使得清除和重绘树的过渡变得无形。 - Coder12345
1个回答

7
您在处于“tsEditing”树状态时无法更改“FocusedNode”,并且在离开“OnNewText”事件之前,您将一直处于该状态。 “OnNewText”本身更多地用于编辑验证;这是一个可以修改编辑值的事件。相反,您应该使用“OnEdited”事件,该事件在实际完成编辑后触发。因此,将数据库更新和树重新加载等内容移至该事件中,如下面的C ++ Builder伪代码所示:
void __fastcall TForm1::VirtualStringTree1Edited(TBaseVirtualTree *Sender, 
  PVirtualNode Node, TColumnIndex Column)
{
  // update your database here; with VirtualStringTree1.Text[Node, Column] you
  // can access the current node text after edit; when you update your DB, call
  InitializeTree();
} 

1
正是我所需要的解释。同时也省去了我通过PostMessage调用来实现它的麻烦。谢谢! - Coder12345

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接