WPF 和 Silverlight 控件和布局平移和缩放功能
我想了解 WPF/Silverlight 布局的一般要求,以便能够实现平移和缩放(拖动和缩放)功能。我的意思不是对图像进行平移和缩放,而是对带有某些控件的整个页面(窗口)布局(或其一部分)进行平移和缩放。
布局的哪些功能以及所使用的自定义控件的哪些功能使布局固定并且无法进行平移和缩放?
I would like to understand the general requirements for WPF/Silverlight layout for making it possible to implement pan&zoom (drag and zoom) features. I don't mean pan&zoom for an image but for a total page (window) layout (or part of it) with some controls.
What features of the layout and what features of used custom controls make layout fixed and pan&zoom impossible?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
一般来说,您可以像对待单个 UIElement 一样对待任何复合 UI 元素集,因此图像的情况与对整个应用程序执行相同操作并没有真正的不同。处理基于用户输入的缩放(与 Viewbox 所做的自动缩放相反)的最佳方法是应用 ScaleTransform。这可以在高级父元素上设置,例如窗口布局根部的网格。对于平移,您可以合并 TranslateTransform,或者在某些情况下使用 ScrollViewer 来处理内容视图的移动。
In general you can treat any composite set of UI elements the same as you would treat a single UIElement so the case of an image isn't really different than doing the same for an entire application. The best way to handle zooming based on user input (as opposed to automatic scaling that Viewbox does) is applying a ScaleTransform. This can be set on a high level parent element, like a Grid at the root of a Window layout. For panning you can combine in a TranslateTransform or in some cases use a ScrollViewer to handle moving the view of the content.
在 XAML 中实现缩放的一种非常简单的方法是使用 Silverlight ViewBox。这会缩放 XAML 而不是像素。您可以指定要使用的拉伸,ViewBox 将根据此进行缩放(填充、无、均匀等)。如果您在 Google 上搜索 Silverlight+Viewbox,网络上会出现一些很棒的 Viewbox 博客文章。
使用类似的拖放机制可以轻松完成平移,并且还有大量关于此的操作方法博客文章,可通过 Google 获取。相当于捕获 MouseDown、MouseMove 和 MouseUp 事件。
One really easy way of implementing zoom in XAML is to use a Silverlight ViewBox. This zooms the XAML not the pixels. You can specify the stretch to use and the ViewBox will scale based on this (Fill, None, Uniform etc). There are some great Viewbox blog posts on the web if you search for Silverlight+Viewbox on Google.
The panning is easily accomplished with a similar mechanism to drag and drop and there are also numerous how-to blog posts on this, available via Google. Just amounts to capturing MouseDown, MouseMove and MouseUp events.
一般规则
除了少数例外,WPF 中的所有内容都可以根据您的喜好进行平移、缩放、旋转、拉伸等。这包括像 Button 这样的单一控件、像 ListBox 这样的复合控件以及像 StackPanel 这样的容器。
例外
以下是例外情况:
如果您使用 Adorner 并且您的 AdornerDecorator 位于平移/缩放区域之外,则附加到平移/缩放区域的 Adorner 将平移但不会缩放。解决方案是在平移/缩放区域内放置一个额外的 AdornerDecorator。
如果您使用 Popup,它将显示在其 PlacementTarget 的平移/缩放位置,但它本身不会缩放。当您平移包含其 PlacementTarget 的区域时,它也不会移动(基本上它位于目标控件上方的自己的表面中)。要解决此问题,当您希望在缩放/平移区域内弹出某些内容时,请使用具有高 Z 顺序的零尺寸画布。
您定义的任何上下文菜单都将显示在弹出窗口中,因此即使您单击的区域放大或缩小,菜单项也将显示正常大小。由于上下文菜单的性质,这可能是理想的行为。如果没有,您可以将菜单项包装在 ViewBox 中,并将缩放与主区域的缩放相关联。
即使 UI 平移或缩放,您的工具提示也将显示正常大小。与 ContextMenu 的解决方案相同。
如果您使用 WinForms 集成来集成旧版 WinForms 控件和 UI,它们在某些情况下将无法正确平移、缩放和剪辑。有一种先进的技术可以解决这个问题,您可以在屏幕外实现 WinForms 控件,然后使用 BitBlt 或类似的方法将图像作为图像复制到窗口中,并将鼠标单击和击键转发到屏幕外窗口。不过,这是一项艰巨的工作。
如果您绕过 WPF 并直接使用 GDI+ 或 DirectX,或使用 Win32 hWnds 来显示内容或 UI,则该内容或 UI 将无法正确平移、缩放或剪切到窗口,除非您自己在界面代码中执行此操作。
最终说明
良好的 WPF UI 始终使用 Grid、DockPanel 等面板以灵活的方式布局控件,以便它们自动调整到容器大小,而不是使用固定的大小和位置。对于平移/缩放区域的内部内容也是如此,但此规则有一个例外:平移/缩放区域中最外面的元素必须具有指定的大小。否则什么将定义被平移/缩放的区域?
实现平移/缩放功能的简单方法是调整平移/缩放区域中最外层控件的 RenderTransform。有许多不同的方法来实现平移和缩放控件,例如,您可以使用工具栏按钮和滑块、滚动条、鼠标滚轮、空格键+拖动进行平移、平移 UI 本身的可拖动区域或这些的任意组合。无论您选择哪个接口,只需让它从代码隐藏中适当更新 RenderTransform 即可。
如果您选择的平移机制是滚动条,您可能希望使用 ScrollViewer 并且仅使用 RenderTransform 进行缩放。
如果您选择的平移机制是滚动
确保在平移/缩放区域上设置剪辑。否则,如果您放大或平移侧面的项目,它们在平移/缩放区域之外仍然可见。
General rule
With few exceptions, everything in WPF can be panned, zoomed, rotated, stretched, etc to your heart's content. This include single controls like Button, compound controls like ListBox, and containers like StackPanel.
The exceptions
Here are the exceptions:
If you are using Adorner and your AdornerDecorator is outside the panned/zoomed area, then the Adorners attached to your panned/zoomed area will pan but not zoom. The solution is to put an additional AdornerDecorator inside the panned/zoomed area.
If you use a Popup, it will display at the panned/zoomed location of its PlacementTarget but it will not itself be scaled. It will also not move as you pan the area containing its PlacementTarget (basically it sits in its own surface above the target control). To get around this, use a zero-size Canvas with high Z order instead when you want something to pop up within the zoom/pan area.
Any ContextMenu you define will be shown inside a popup, so the menu items will display normal size even when the area you clicked on is zoomed in or out. Because of the nature of a context menu, this is probably desirable behavior. If not, you can wrap the menu items in a ViewBox and tie the zoom to your main area's zoom.
Your ToolTips will display normal size even if the UI is panned or zoomed. Same solution as for ContextMenu.
If you used WinForms integration to integrated legacy WinForms controls and UI, they will not properly pan, zoom and clip in certain situations. There is an advanced technique for working around this, where you implement the WinForms control off-screen, then using BitBlt or similar copy the image into your window as an image, and forward mouse clicks and keystrokes to the offscreen window. This is a lot of work, though.
If you bypass WPF and directly use GDI+ or DirectX, or use Win32 hWnds to display content or UI, that content or UI will not be properly panned, zoomed or clipped to the window unless you do it yourself in your interface code.
Final notes
A good WPF UI always uses panels like Grid, DockPanel, etc to lay out controls in a flexible manner so they automatically adjust to container sizes, rather than using fixed sizes and positions. This is also true for the internal contents of your pan/zoom area as well, BUT there is an exception to this rule: the outermost element in your pan/zoom area must have a specified size. Otherwise what will define the area being panned/zoomed over?
The easy way to implement pan/zoom capabilities is to adjust the RenderTransform of the outermost control in your pan/zoom area. There are many different ways to implement controls for panning and zooming, for example you could use toolbar buttons and sliders, scroll bars, mouse wheel, spacebar+drag to pan, draggable areas of panned UI itself, or any combination of these. Whichever interface you choose, just have it update the RenderTransform appropriately from the code-behind and you're good to go.
If your chosen panning mechanism is scroll bars, you might want to use a ScrollViewer and only use the RenderTransform for the zoom.
Be sure you set clipping on the pan/zoom area. Otherwise if you zoom in or pan items off the side, they will still be visible outside the pan/zoom area.
使用 MultiScaleImage 或 Canvas 区域,并放置平移和缩放所需的所有内容
在代码中使用 TranslateTransform 和 TransformGroup 中的 ScaleTransform 来平移和缩放
查看其他 SO 帖子 或此 示例 或 这个
Use a MultiScaleImage or Canvas area, and place everything you need to pan and zoom in it
In code use make a TranslateTransform and a ScaleTransform in a TransformGroup to pan and zoom
Check out other SO post or this example or this one