【问题标题】:Limit drag and drop to within a single control将拖放限制在单个控件内
【发布时间】:2025-12-08 10:05:01
【问题描述】:

我看了又看,找不到答案。

我有一个TreeView。它具有拖放功能以允许在树中移动节点。

我想将拖放限制为仅在该一个控件中、在应用程序的单个实例中工作(应用程序本身可以运行多个实例)。

我尝试了以下方法:

private void SubFolderTreeView_DragEnter(object sender, DragEventArgs e)
{
    TreeView source = sender as TreeView;    // also tried = (TreeView) sender;

    if (source == this.SubFolderTreeView && e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
        e.Effect = DragDropEffects.Move; // Okay, set the visual effect
    else
        e.Effect = DragDropEffects.None; // Unknown data, ignore it
}

不幸的是,同一应用程序的第二个实例仍然能够从其TreeView 拖动到第一个 TreeView:(source == this.SubFolderTreeView) 是真的

我没有测试一个完全不同的树视图是否可以拖到我的,虽然我怀疑它,但上述行为已经失败了。

我尝试了其他一些方法 - 比较表单或控件的句柄也不起作用

bool isSameForm = ((MyForm) source.TopLevelControl == this);    // still true
bool isSameHandle = (((Control)source).Handle == ((Control)this.SubFolderTreeView).Handle);     // still true

我能想到的唯一其他事情是存储在 TreeView 或 Form 中的随机数(可能不起作用),并检查控件的绝对屏幕位置(不是最好的)方法)。

我当然可以在应用程序中添加一个互斥体,因此只允许一个实例运行,但我宁愿不这样做。

任何人都可以提出一个好的方法吗?

【问题讨论】:

  • 我建议使用互斥锁..你为什么这么犹豫要使用互斥锁..?
  • 无法获取拖拽源,D+D合约的一部分。一个非常简单的解决方法是在表单类中使用私有字段。在调用 DoDragDrop 之前分配它,之后将其设置回 null。当 TreeNode 来自另一个进程时,它将为 null。
  • 谢谢,汉斯,这行得通,一个很好的解决方案,你是一个救生员。
  • DJ Kraze,也许我误解了 Mutex 的使用,但我一直认为它们被用来防止多个应用程序实例同时运行,这是我不想要的(我想要1 个或更多运行)。有没有办法可以针对这种情况使用它? (为了学习,问了更多,因为 Hans 已经提供了一个可行的解决方案)
  • @JamesCarlyle-Clarke - 互斥锁用于防止同时访问共享资源,其中“共享资源”具有非常非常广泛的定义。 “开始许可”是一个例子(即,只有一个程序实例正在运行),但“接受拖放的许可”是另一个例子。您的程序将尝试获取互斥锁,如果成功,则设置放置处理程序可以看到的标志。删除完成后,程序将释放互斥体。

标签: c# winforms drag-and-drop instance


【解决方案1】:

为了充实 Hans Passant 的解决方案(非常有效,感谢 Hans)以供将来参考和其他搜索者解决这个问题,我使用了代码:

// prevents dragging from other instances of this form - thanks to Hans Passant
private bool DragDropFromThisForm = false;

private void SubFolderTreeView_ItemDrag(object sender, ItemDragEventArgs e)
{
    // Initiate drag/drop
    DragDropFromThisForm = true;
    DoDragDrop(e.Item, DragDropEffects.Move);
    DragDropFromThisForm = false;
}

private void SubFolderTreeView_DragEnter(object sender, DragEventArgs e)
{
    MyForm form = (MyForm) (sender as TreeView).TopLevelControl;

    if (form.DragDropFromThisForm && e.Data.GetDataPresent("System.Windows.Forms.TreeNode", false))
        e.Effect = DragDropEffects.Move; // Okay, set the visual effect
    else
        e.Effect = DragDropEffects.None; // Unknown data, ignore it
}

很可能 DJ Kraze 的回答也行得通,而且可能更优雅一点,但 Hans 的解决方案是轻量级且有效的。

【讨论】:

    【解决方案2】:

    我并没有真正遵守限制,您提供的信息似乎有缺陷的逻辑(所有相同的实例,但只有一个可以拖放 - 什么??),但有一些建议:

    • 具有确定节点是否可以“拖放”的属性,并且只能在一个实例中设置。
    • 仅在您希望能够“拖放”的一个实例上订阅事件。
    • 创建一个单独的支持拖放的 TreeView 类,并在其他任何地方实例化基 TreeView。

    【讨论】:

    • 可能我没有解释清楚。该窗体有一个 TreeView。我希望 TreeView 中的节点可以在该 TreeView 中拖动,但不能再拖动(不能拖动到其他表单,也不能拖动到我的表单的其他实例)。所以Form A中的TreeView A只能拖拽TreeView A内的节点。 Form B 中的 TreeView B 只能拖动 TreeView B 内的节点。 TreeView A 不能将节点拖到 TreeView B,反之亦然。