单击 TreeView 上的 ListView(在 TForm 上)会触发 TreeView OnChange 事件

Posted

技术标签:

【中文标题】单击 TreeView 上的 ListView(在 TForm 上)会触发 TreeView OnChange 事件【英文标题】:Click on TListView (on TForm) over TTreeView triggers TTreeView OnChange event 【发布时间】:2017-04-25 11:40:05 【问题描述】:

使用 Borland C++ Builder

我有一个应用程序,它带有一个自行创建的面包屑控件,位于TTreeView 控件的正上方,TListView 控件(alClientTForms 内)作为下拉菜单。

控件的位置是新的。意思是,实现较旧,并且以前运行良好,但我只是将面包屑控件移到 TTreeView 控件上方。

我现在注意到,从面包屑控件中单击 TListView 项目,会触发 TTreeView OnChange 事件! 并且选中的节点是最上面的节点。

这会导致我的设计发生冲突,因为TListView 项目单击和TTreeView Onchange 会导致显示/完成不同的事情。 哪个事件最终“获胜”也不是 100%,因此结果也是可变的。

我可以考虑在面包屑代码执行时尝试禁用TTreeView,以便忽略点击,但我想知道这是正常现象还是我遗漏了什么?

两个控件在相互显示时触发是否正常?

ListView 项目点击是通过分配以下函数的 OnClick 事件处理的:

void __fastcall TBreadcrumbListViewEx::InternalListViewClick(TObject *Sender)


if (FListView->Selected && FOnBreadcrumbListItemClick)
    
    TPoint pt;
    pt = FListView->ScreenToClient(Mouse->CursorPos);

    THitTests HitTests = FListView->GetHitTestInfoAt(pt.x, pt.y);
    if ( (HitTests.Contains(htOnIcon)) || (HitTests.Contains(htOnItem)) ||
         (HitTests.Contains(htOnLabel)) || (HitTests.Contains(htOnStateIcon)) )
        
        // This is a very precarious situation, because the FOnBreadcrumbListItemClick callback will most likely trigger building a new Breadcrumbs layout (Different Items)
        // Which causes OnData events from this control to want to access Breadcrumbs that have been deleted already
        // Therefore, get the selected Item (which will trigger OnData) and use its Index and Data members

        TListItem *Item = FListView->Selected ;

        // Next prevent any OnData event to get through and wreak havoc
        // This needs to be done *before* Hide() is called

        _releasing = true ;

        // Hide is necesary because if the FOnBreadcrumbListItemClick event takes a long time, for instance it shows a dialog,
        // this control will still be visible, yet it cannot show proper content anymore, since the OnData event is disabled
        // Hence Hide() to no show it anymore when, for instance, a dialog is showing

        Hide() ;

        FOnBreadcrumbListItemClick(this, Item->Index, BreadcrumbItem, Item->Data);

        Release(); // Close();
        
    


我一直在运行测试。 首先,我禁用了事件/回调FOnBreadcrumbListItemClick(),但问题仍然存在,即使单击该项目时没有真正发生任何事情。 我一直在尝试禁用Hide()Release(),将Release() 替换为Close() 等。但如果其中一个仍然存在,问题仍然存在。

只有当我同时禁用 Hide()Release()/Close() 时,我似乎不会遇到麻烦。直到那时我才在我的测试中不再看到TTreeView OnChange 出现。老实说,我现在什么都不确定了。

这是你所期望的吗?或者这仅仅是一个副作用,问题仍然是别的?

另外......我将如何执行回调并从视图中隐藏/删除弹出窗口,而不会“不安”TTreeView

暂时禁用TTreeView 仍然是一种选择,但我宁愿修复这个问题,以免下次我将控件移动到其他位置时再次遇到麻烦。

【问题讨论】:

这意味着您的面包屑代码不会吞噬弹出表单上的鼠标单击,而是允许它传递到下面的 TreeView。这不是默认情况下 UI 的操作方式,这意味着您的面包屑可能会做一些不应该做的额外事情。但是您没有提供minimal reproducible example 来重现该问题,因此我投票决定将此作为题外话结束。 (ListItem)单击触发了一个重新填充面包屑控件的事件。深入挖掘我看到面包屑Enabled 设置为false 一段时间,在此过程中,以避免在冗长的过程中更多的点击。因此,这会在 Onclick 事件期间发生。这可能是面包屑控件不吞下鼠标点击的原因吗?我会再做一些测试。 @RemyLebeau - 今天经过更多测试后,我觉得可以在我认为存在问题的地方发布代码。感谢您的意见。 没有。我将不得不撤回或编辑我自己的答案。虽然下面的代码效果更好,但问题仍然存在。我现在改变了我的调试方法,并专注于TTreeView 而不是面包屑控件,我注意到问题从调用TreeView->ClearSelection() 的那一刻开始。该调用将 TreeView 设置为一种状态(不知何故),它突然也捕获了上面显示的控件的点击。对于未调用 ClearSelection() 的情况,所有工作都按预期工作并继续按预期工作。发现这一点也解释了我一直看到的可变性 现阶段我必须假设这是一个 VCL 问题。 【参考方案1】:

我最终通过将消息发布到队列末尾来“修复”它,以便 TListView OnClick 事件可以尽快完成,并且鼠标事件完全被吸收,通过避免 @ OnClick 活动期间的 987654323@ 或 Release()

void __fastcall TBreadcrumbListViewEx::InternalListViewClick(TObject *Sender)


if (FListView->Selected && FOnBreadcrumbListItemClick)
    
    TPoint pt = FListView->ScreenToClient(Mouse->CursorPos);

    THitTests HitTests = FListView->GetHitTestInfoAt(pt.x, pt.y);
    if ( (HitTests.Contains(htOnIcon)) || (HitTests.Contains(htOnItem)) ||
         (HitTests.Contains(htOnLabel)) || (HitTests.Contains(htOnStateIcon)) )
        
        PostMessage(Handle /*Destination*/, WM_APP, 0, (LPARAM)FListView->Selected);
        
    



//------------

void __fastcall TBreadcrumbListViewEx::WmApp(TMessage &Msg)


TListItem *ClickedItem = (TListItem*)Msg.LParam ;

if (ClickedItem)
    

    // Next prevent any more OnData events to get through and wreak havoc
    // This needs to be done *before* Hide() is called

    _releasing = true ;

    // Hide is necesary because if the FOnBreadcrumbListItemClick event takes a long time, for instance it shows a dialog,
    // this control will still be visible, yet it cannot show proper content anymore, since the OnData event is disabled
    // Hence Hide() to no show it anymore when, for instance, a dialog is showing

    Hide() ;

    // Disable the entire breadcrumb control to avoid clicks while a dialog is up or during a lengthy process to get the data for next
    // breadcrumb layout

    BBar->Enabled = false ;

    FOnBreadcrumbListItemClick(this, ClickedItem->Index, BreadcrumbItem, ClickedItem->Data);

    BBar->Enabled = true ;

    Release();
    


【讨论】:

以上是关于单击 TreeView 上的 ListView(在 TForm 上)会触发 TreeView OnChange 事件的主要内容,如果未能解决你的问题,请参考以下文章

单击 ListView 上的删除按钮后删除 ListView 上的项目

单击 ListView 上的侦听器

在用户单击 listView 项目上的按钮的 Firebase 数据库上写入

Delphi Listview 和 TreeView 中文切换乱码问题

c# winform 里TreeView的使用

winform 自定义控件,继承自ListView 和treeview 怎么做,一般用于做权限操作