gpt4 book ai didi

wpf - 在其 winforms 父级中拖动托管 WPF 控件吞噬的事件

转载 作者:行者123 更新时间:2023-12-02 01:18:51 25 4
gpt4 key购买 nike

我有一个需要以特定方式处理拖动事件的 winforms 控件。此控件包含许多子控件 - 其中一些是嵌入在 ElementHost 对象中的 WPF。它是最顶层的 winforms 控件,需要处理拖动事件。

当鼠标悬停在父级的 winforms 组件上时,我可以轻松地使拖动行为起作用。没有什么特别需要我做的。但是,WPF 控件是黑洞 - 它们似乎吞噬了拖动事件,从而阻止了主 winforms 控件正确处理它们。

我可以通过以下方式解决这个问题:

  1. 在WPF控件中设置AllowDrop = true
  2. 覆盖 OnDragEnter WPF 控件
  3. ElementHost 开始递归地遍历 Control.Parent 引用,直到找到我需要的顶级控件
  4. 在找到的顶级控件上调用 IDropTarget.OnDragEnter

如您所想,这很丑陋:

public MyWPFControl()
{
InitializeComponent();
_host = new Lazy<System.Windows.Forms.Control>(BuildHostControl);
AllowDrop = true;
}

private System.Windows.Forms.Control BuildHostControl()
{
return new System.Windows.Forms.Integration.ElementHost
{
Dock = System.Windows.Forms.DockStyle.Fill,
Child = this,
};
}

protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
if (e.Data.GetDataPresent(typeof(MyDragObject)))
{
var args = ConvertDragArgs(e);
var control = GetControl(_host.Value)
((System.Windows.Forms.IDropTarget)control).OnDragEnter(args);
}
}

private System.Windows.Forms.DragEventArgs ConvertDragArgs(DragEventArgs e)
{
var dragObject = e.Data.GetData(typeof(MyDragObject)) as MyDragObject;
var position = e.GetPosition(this);
var data = new System.Windows.Forms.DataObject(dragObject);
return new System.Windows.Forms.DragEventArgs(data, (int)e.KeyStates, (int)position.X, (int)position.Y, System.Windows.Forms.DragDropEffects.Copy, System.Windows.Forms.DragDropEffects.Copy);
}

private MyTopLevelControl GetControl(System.Windows.Forms.Control control)
{
var cell = control as MyTopLevelControl;
if (cell != null)
return cell;

return GetControl(control.Parent);
}

OnDragDrop 也需要做类似的工作,但为了简洁起见,我省略了它

当然,我可以注入(inject)一个辅助对象,顶级控件和 WPF 控件都引用了该对象,这样我就可以消除控件遍历并稍微清理一下。

但是,如果我可以避免的话,我真的不想处理 WPF 控件中的拖动事件 - 我宁愿顶级控件负责并处理它在我拖动它的 winforms 时所做的事情 child 控制。有办法实现吗?

最佳答案

您所描述的是完全正常的。以极快的速度:当您将 AllowDrop 属性设置为 True 时,Winforms 和 WPF 都会调用 RegisterDragDrop()通讯功能。您将识别 IDropTarget 接口(interface)的方法,它们直接映射到等效的 .NET 事件。请注意 HWND 参数,这是此行为开始的地方。

在 Winforms 中,每个 Control 都有自己的 Handle 属性。所以没有什么特别的事情发生,事件是在悬停/放下的控件上引发的。这在 WPF 中不起作用,控件没有句柄,因此它们借用外部容器的句柄。通常是 Window 对象的 HWndSource。然后必须将事件定向到适当的控件,这就是路由事件所做的。关键区别在于事件似乎从内部控件“冒泡”到外部控件。在 Winforms 或 COM 管道中没有这样的冒泡。因此,如果嵌入式 WPF 控件没有用于拖动的用途,那么这就是降压停止的地方,用户将获得“无拖动”鼠标光标。就像在任何 Winforms 应用程序中发生的一样。

Winforms 程序员通常通过给 UI 一个明显的拖动目标来处理这个问题。类似于“放在这里”字形的东西。这并没有完全损害可用性,拖放功能往往很难发现。如果这不是您想要的,那么您唯一的选择就是自己冒泡事件。就像您一直在做的那样。

您需要的大部分内容已经存在,只是您没有以最佳方式执行此操作。最明显的方法是在 ElementHost 中进行这些冒泡。所有这些控件的共同特征。这很容易做到,95% 的 Winforms 问题都是通过从 .NET 类派生您自己的类来解决的。一些可玩的代码:

using System;
using System.Windows.Forms;
using System.Windows.Forms.Integration;

class ElementHostEx : ElementHost {
public ElementHostEx() {
this.ChildChanged += ElementHostEx_ChildChanged;
}

private void ElementHostEx_ChildChanged(object sender, ChildChangedEventArgs e) {
var prev = e.PreviousChild as System.Windows.UIElement;
if (prev != null) {
prev.DragEnter -= Child_DragEnter;
prev.Drop -= Child_Drop;

}
if (this.Child != null) {
this.Child.DragEnter += Child_DragEnter;
this.Child.Drop += Child_Drop;
}
}
// etc...
}

我没有发布 Child_Xxxx 事件处理程序,您已经有了该代码。编译并从工具箱顶部放置新控件,替换现有的 ElementHosts。或者,在代码中,创建一个 ElementHostEx 而不是 ElementHost。

关于wpf - 在其 winforms 父级中拖动托管 WPF 控件吞噬的事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41349364/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com