WPF - 如何最好地实现具有可拖动/可缩放子项的面板?

发布于 2024-08-04 13:07:36 字数 1665 浏览 4 评论 0原文

我正在尝试修改 Graph# 库的默认图形查看器,因为它的用户界面很糟糕(只需尝试将节点拖动到边界之外,您就会看到!)

基本设置是这样的:有一个 GraphCanvas 控件(继承自 Panel),它具有 Vertex 和 Edge 控件类型的子代。我想要实现的是:

  • 如果内容不适合屏幕,GraphCanvas 有滚动条;
  • GraphCanvas 还可以通过“拖动”来滚动(只需单击空白区域并拖动);
  • GraphCanvas可以放大和缩小(通过CTRL+鼠标滚轮);
  • 顶点可以被拖动。如果将顶点拖到 GraphCanvas 当前边界之外,则边界会增加。滚动条应该反映这一点,但是在拖动顶点时当前视口不应滚动。如果拖动顶点会减少 GraphCanvas 的边界,情况也是如此 - 它应该保持相同的大小,直到拖动操作完成,然后才调整大小。在拖动操作期间自动滚动视口非常令人困惑,并且很容易引入拖动错误。如果您想了解我的意思,请参阅原始实现。

尽管我已经有相当多的 .NET 经验,但我在 WPF 方面仍然是一个完全的初学者。我当前的尝试是(在测量/排列布局阶段)为每个顶点提供所需的 XY 坐标(即使为负),并通过处理 GraphCanvas 上的鼠标事件和修改 RenderTransform 属性来实现缩放/滚动。拖动只会更改特定顶点上的 XY 坐标(可能会触发整个事物的重新布局,这也最好避免)。滚动条是通过将 GraphCanvas 放置在 ScrollViewer 中并在 GraphCanvas 上实现 IScrollInfo 来实现的。

不幸的是,似乎有一个问题:只有当 GraphCanvas 本身有背景时,我才能在它本身上获取鼠标事件。没关系,无论如何我想要一个白色背景,但是在 GraphCanvas 的负坐标中它不会绘制背景 - 因此不会响应鼠标事件。

我还想知道允许所有子控件(顶点和边)进入负坐标是否是正确的做法。你会如何实施这个?


Added: To clarify about the background problem check out the following screenshot:
(source: valts.21.lv)

您在这里看到的是一个简单的 Windows 窗体窗体,其上有一个 WPF 主机控件。其中有一个 ScrollViewer,而 ScrollViewer 中有 GraphCanvas。 GraphCanvas 包含 4 个顶点和 6 个边。

GraphCanvas 被拉伸以填充 ScrollViewer。但由于某些顶点位于负坐标,因此它应用了 RenderTransform,它只是将所有内容向右移动(TranslateTransform)。它还有一个白色背景画笔。

注意左侧的灰色区域。它仍然是 GraphCanvas 的一部分,但背景画笔不知何故没有延伸到那里。另外,如果我用鼠标左键单击那里(不是在节点上,而是在灰色区域上),我不会收到事件。如果我左键单击白色区域,我就可以正常获取所有事件。

I'm trying to modify the default graph viewer of the Graph# library because its user interface is awful (just try dragging a node outside of the boundaries, you'll see!)

The basic setup is this: there is a GraphCanvas control (inherited from Panel) which has children of Vertex and Edge control types. What I want to achieve is:

  • GraphCanvas has scroll bars if the contents do not fit in the screen;
  • GraphCanvas can also be scrolled by "dragging" it (just click on an empty space and drag);
  • GraphCanvas can be zoomed in and out (via CTRL+mouse wheel);
  • Vertices can be dragged around. If a vertex is dragged outside the current boundaries of GraphCanvas, the boundaries are increased. The scroll bars should reflect this, however the current viewport should not scroll away while the vertex is being dragged . The same goes if dragging a vertex reduces the boundaries of GraphCanvas - it should stay the same size until the drag operation is finished and resize only then. Automatically scrolling the viewport during a drag operation is awfully confusing and easily introduces dragging errors. See the original implementation if you want to know what I mean.

Although I've got a fair bit of experience with .NET, I'm still a complete beginner in WPF. My current attempt is (in the measure/arrange layout phase) to give each vertext the XY coordinate it desires (even if negative) and implement zooming/scrolling by handling mouse events on the GraphCanvas and modifying the RenderTransform property. Dragging just changes the XY coordinates on the specific vertex (probably triggering the re-layout of the whole thing which would be nice to avoid too). Scrollbars are implemented by placing the GraphCanvas inside a ScrollViewer and implementing IScrollInfo on the GraphCanvas.

Unfortunately there seems to be a problem: I can get mouse events on the GraphCanvas itself only if it has a background at the point. That would be OK, I want a white background anyway, but in the negative coordinates of the GraphCanvas it does not draw the background - and thus does not respond to mouse events.

I'm also wondering if I'm doing the Right Thing by allowing all my child controls (vertices and edges) to go into negative coordinates. How would you implement this?


Added: To clarify about the background problem check out the following screenshot:

(source: valts.21.lv)

What you see here is a simple Windows Forms form with a WPF Host control on it. That has a ScrollViewer in it, and the ScrollViewer has the GraphCanvas in it. The GraphCanvas contains 4 vertices and 6 edges.

The GraphCanvas is stretched to fill the ScrollViewer. But since some of the vertices are at negative coordinates, it has a RenderTransform applied which simply shifts everything to the right (TranslateTransform). It also has a white background brush.

Note the gray area on the left. That's still a part of the GraphCanvas, but the background brush somehow doesn't exted there. Also, if I left-click there with my mouse (not on a node, but on the gray area), I do NOT get an event. If I left-click on the white area, I get all events just fine.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

末骤雨初歇 2024-08-11 13:07:36

在 canvas.mouseDown 上调用 CaptureMouse 和 < a href="http://msdn.microsoft.com/en-us/library/system.windows.uielement.releasemousecapture.aspx" rel="nofollow noreferrer">ReleaseMouseCapture 鼠标松开。另外,如果您将画布背景设置为透明,它仍然可以进行测试

Call CaptureMouse on canvas.mouseDown and ReleaseMouseCapture on mouse up. Also, if you set your canvas background to transparent it will still be hit testable

神回复 2024-08-11 13:07:36

您可以将“可拖动”行为附加到每个元素。

You can attach a 'Draggable' behavior to each element.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文