C# - Winforms - 触摸拖动时不显示自定义拖动图像
C# - Winforms - Custom Drag Image is Not Displayed When Dragging by Touch
我有一个用 C# 编写的具有自定义拖放功能的旧 WinForms 应用程序。此应用程序在触摸屏设备(例如 Surface Pro 3)上 运行 时会出现问题。
基本上有一个树视图控件,允许将项目拖到应用程序的不同区域并完成一些计算。如果我使用鼠标或手写笔,则会在屏幕上绘制自定义拖动图像。如果我使用触摸拖动项目,图像不会显示,但代码会正确执行,包括自定义光标的绘制。
似乎自定义拖动图像没有显示,因为鼠标光标被O.S隐藏了。在触摸拖动操作期间。如何显示拖动图像?
更新
这是一些代码来演示我要修复的问题。创建一个新的 WinForms 应用程序,向其添加一个树视图并连接事件。您会注意到,如果您使用手写笔或鼠标,拖动操作将显示一个图标。如果您触摸并拖动某个项目,则不会显示任何内容。
public Form1()
{
InitializeComponent();
TreeNode node = new TreeNode("welp");
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void treeView1_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = true;
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
((TreeView)sender).DoDragDrop(e.Item, DragDropEffects.Move);
}
好的,所以我完全按照我想要的方式工作了。这并不容易,它可能不是解决它的首选方法,但它做了我想要的,我正赶时间完成它。
首先,我找到了这篇文章Shell Style Drag and Drop。它在大多数情况下都有效,但它是大约 7 年前写的,并没有考虑 O.S 是如何工作的。已经改变。关于它的细节以及为什么它对我不起作用的细节太多了。
我试图解决的主要问题是我原来的拖放代码使用了在拖放操作的 GiveFeedback 事件中绘制新光标的老派方法。由于 Windows O.S,在触摸设备上工作得很好。在触摸拖动操作期间隐藏光标,这样我自定义绘制的光标将永远不会显示。一些(轻度使用)Google 搜索显示 Windows 不允许两个指针设备同时处于活动状态,即使调用本机 ShowCursor 方法也是如此。很公平。
在拖放操作中尝试在屏幕上绘图的最大问题是大多数发送到 window 的消息,例如 WM_MOUSEMOVE 和 WM_PAINT被暂停。我可以画任何东西的唯一方法是在 GiveFeedback 事件中。
所以,我发现这个 article 涵盖了使用透明覆盖层绘制的内容。同样,它并没有完全按照我的要求进行操作,但稍加调整就可以完美运行。我能够获取叠加层的 HDC 和我的表单的 HDC,并执行一个很好的 BitBlt 操作以在屏幕上显示我的自定义位图,并通过获取表单的底层图像来擦除旧绘图。我还将绘制自定义光标的方法更改为绘制自定义位图的方法。
这是我对上述文章所做的更改
this.BackColor = Color.White;
this.Opacity = 1; // Tweak as desired
this.TransparencyKey = Color.White;
以及我为允许消息泵通过而添加的内容
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= Win32.WS_EX_TRANSPARENT;
return createParams;
}
}
其余的细节并不过分复杂,但需要很好地混合拖放操作、叠加、绘图和擦除以及双缓冲。在某些情况下,就像我的情况一样,我在拖动时更新了 TreeView 控件,我必须扩展绘制操作的矩形,以便 bitblt 图像覆盖我绘图之外的区域。
如果您对本文感兴趣并想了解我做了什么以及如何完成它,请在评论中告诉我,我将 post 编写代码。
试试这个代码
希望对你有帮助
public Form1()
{
InitializeComponent();
TreeNode node;
for (int x = 0; x < 3; ++x)
{
node = treeView1.Nodes.Add(String.Format("Node{0}", x * 4));
for (int y = 1; y < 4; ++y)
{
node = node.Nodes.Add(String.Format("Node{0}", x * 4 + y));
}
}
treeView1.AllowDrop = true;
treeView1.Dock = DockStyle.Fill;
treeView1.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag);
treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
treeView1.DragOver += new DragEventHandler(treeView1_DragOver);
treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
else if (e.Button == MouseButtons.Right)
{
DoDragDrop(e.Item, DragDropEffects.Copy);
}
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void treeView1_DragOver(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
treeView1.SelectedNode = treeView1.GetNodeAt(targetPoint);
}
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (!draggedNode.Equals(targetNode) && !ContainsNode(draggedNode, targetNode))
{
if (e.Effect == DragDropEffects.Move)
{
draggedNode.Remove();
targetNode.Nodes.Add(draggedNode);
}
else if (e.Effect == DragDropEffects.Copy)
{
targetNode.Nodes.Add((TreeNode)draggedNode.Clone());
}
targetNode.Expand();
}
}
private bool ContainsNode(TreeNode node1, TreeNode node2)
{
if (node2.Parent == null) return false;
if (node2.Parent.Equals(node1)) return true;
return ContainsNode(node1, node2.Parent);
}
我有一个用 C# 编写的具有自定义拖放功能的旧 WinForms 应用程序。此应用程序在触摸屏设备(例如 Surface Pro 3)上 运行 时会出现问题。
基本上有一个树视图控件,允许将项目拖到应用程序的不同区域并完成一些计算。如果我使用鼠标或手写笔,则会在屏幕上绘制自定义拖动图像。如果我使用触摸拖动项目,图像不会显示,但代码会正确执行,包括自定义光标的绘制。
似乎自定义拖动图像没有显示,因为鼠标光标被O.S隐藏了。在触摸拖动操作期间。如何显示拖动图像?
更新 这是一些代码来演示我要修复的问题。创建一个新的 WinForms 应用程序,向其添加一个树视图并连接事件。您会注意到,如果您使用手写笔或鼠标,拖动操作将显示一个图标。如果您触摸并拖动某个项目,则不会显示任何内容。
public Form1()
{
InitializeComponent();
TreeNode node = new TreeNode("welp");
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
treeView1.Nodes.Add(node);
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = DragDropEffects.Move;
}
private void treeView1_GiveFeedback(object sender, GiveFeedbackEventArgs e)
{
e.UseDefaultCursors = true;
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
((TreeView)sender).DoDragDrop(e.Item, DragDropEffects.Move);
}
好的,所以我完全按照我想要的方式工作了。这并不容易,它可能不是解决它的首选方法,但它做了我想要的,我正赶时间完成它。
首先,我找到了这篇文章Shell Style Drag and Drop。它在大多数情况下都有效,但它是大约 7 年前写的,并没有考虑 O.S 是如何工作的。已经改变。关于它的细节以及为什么它对我不起作用的细节太多了。
我试图解决的主要问题是我原来的拖放代码使用了在拖放操作的 GiveFeedback 事件中绘制新光标的老派方法。由于 Windows O.S,在触摸设备上工作得很好。在触摸拖动操作期间隐藏光标,这样我自定义绘制的光标将永远不会显示。一些(轻度使用)Google 搜索显示 Windows 不允许两个指针设备同时处于活动状态,即使调用本机 ShowCursor 方法也是如此。很公平。
在拖放操作中尝试在屏幕上绘图的最大问题是大多数发送到 window 的消息,例如 WM_MOUSEMOVE 和 WM_PAINT被暂停。我可以画任何东西的唯一方法是在 GiveFeedback 事件中。
所以,我发现这个 article 涵盖了使用透明覆盖层绘制的内容。同样,它并没有完全按照我的要求进行操作,但稍加调整就可以完美运行。我能够获取叠加层的 HDC 和我的表单的 HDC,并执行一个很好的 BitBlt 操作以在屏幕上显示我的自定义位图,并通过获取表单的底层图像来擦除旧绘图。我还将绘制自定义光标的方法更改为绘制自定义位图的方法。
这是我对上述文章所做的更改
this.BackColor = Color.White;
this.Opacity = 1; // Tweak as desired
this.TransparencyKey = Color.White;
以及我为允许消息泵通过而添加的内容
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= Win32.WS_EX_TRANSPARENT;
return createParams;
}
}
其余的细节并不过分复杂,但需要很好地混合拖放操作、叠加、绘图和擦除以及双缓冲。在某些情况下,就像我的情况一样,我在拖动时更新了 TreeView 控件,我必须扩展绘制操作的矩形,以便 bitblt 图像覆盖我绘图之外的区域。
如果您对本文感兴趣并想了解我做了什么以及如何完成它,请在评论中告诉我,我将 post 编写代码。
试试这个代码 希望对你有帮助
public Form1()
{
InitializeComponent();
TreeNode node;
for (int x = 0; x < 3; ++x)
{
node = treeView1.Nodes.Add(String.Format("Node{0}", x * 4));
for (int y = 1; y < 4; ++y)
{
node = node.Nodes.Add(String.Format("Node{0}", x * 4 + y));
}
}
treeView1.AllowDrop = true;
treeView1.Dock = DockStyle.Fill;
treeView1.ItemDrag += new ItemDragEventHandler(treeView1_ItemDrag);
treeView1.DragEnter += new DragEventHandler(treeView1_DragEnter);
treeView1.DragOver += new DragEventHandler(treeView1_DragOver);
treeView1.DragDrop += new DragEventHandler(treeView1_DragDrop);
}
private void treeView1_ItemDrag(object sender, ItemDragEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
DoDragDrop(e.Item, DragDropEffects.Move);
}
else if (e.Button == MouseButtons.Right)
{
DoDragDrop(e.Item, DragDropEffects.Copy);
}
}
private void treeView1_DragEnter(object sender, DragEventArgs e)
{
e.Effect = e.AllowedEffect;
}
private void treeView1_DragOver(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
treeView1.SelectedNode = treeView1.GetNodeAt(targetPoint);
}
private void treeView1_DragDrop(object sender, DragEventArgs e)
{
Point targetPoint = treeView1.PointToClient(new Point(e.X, e.Y));
TreeNode targetNode = treeView1.GetNodeAt(targetPoint);
TreeNode draggedNode = (TreeNode)e.Data.GetData(typeof(TreeNode));
if (!draggedNode.Equals(targetNode) && !ContainsNode(draggedNode, targetNode))
{
if (e.Effect == DragDropEffects.Move)
{
draggedNode.Remove();
targetNode.Nodes.Add(draggedNode);
}
else if (e.Effect == DragDropEffects.Copy)
{
targetNode.Nodes.Add((TreeNode)draggedNode.Clone());
}
targetNode.Expand();
}
}
private bool ContainsNode(TreeNode node1, TreeNode node2)
{
if (node2.Parent == null) return false;
if (node2.Parent.Equals(node1)) return true;
return ContainsNode(node1, node2.Parent);
}