WPF drag & drop between containers - wpf

I've got a container, say a Grid. It has two containers, say StackPanels.
StackPanel #1 has some rectangles. What's the best way to enable a user to drag a rectangle and drop it in the StackPanel #2 (and have that rectangle be a child of StackPanel #2).
I've got the drag bit sussed - the only thing that's unclear is whether to actually use containers or just handle all the rectangles manually in a straight canvas.

As far as the logic behind actually moving the items goes, a StackPanel as a container is just fine. If you want to preview the item whilst it's being dragged, you can implement an Adorner that displays the Visual whilst it is being dragged.

Related

Silverlight Drag and Drop (without a Canvas)

I'm trying to drag and drop (slide) a Silverlight element from one part of a window to another.
I've implemented the MouseLeftButtonDown, MouseMove, and MouseLeftButtonUp event handlers on the element, but I've run into a bit of a problem.
All of the examples I've seen involve moving the element by setting the Canvas.Left and Canvas.Top properties. None of the elements I'm trying to manipulate live inside a Canvas. Is there a way to set the absolute position of the element being dragged, based on the coordinates of the mouse? Or is there a prepackaged solution to this problems somewhere that I've missed?
All panels but Canvas use some kind of constraint to position their children. Only Canvas lets you use absolute positioning. That's why I think it is the only way to implement drag and drop .
Feel free to use a Canvas on top of your existing panel. Just remember to remove the dragged element from its original parent and put it in the Canvas (or drag some kind of copy) and do the reverse on mouse up.
One way of achieving absolutely positioning an item inside any container, not only Canvas, is to use transformation instead of Left/Top properties. For instance, to set at Left=50 Top=80 you can modify the margin values through transformation.

How can I animate a transition when moving a child from one panel to another in Silverlight

I would like to animate a transition when moving content between two panels. I am getting a bit map image of a detail record and docking it as a thumbnail in the panel below. The docking area is in a footer grid and the content detail is in another grid that sits above the dock area (the dock and the main content area live in separate rows of the root layout control - another grid).
I have tried implementing this with a ScaleTransform and a TranslateTransform, simultaneously shrinking the image and moving it towards the footer control. When it moves into the footer control, it gets clipped even though the image Canvas.ZIndex property is set to a very high number. Eventually the thumnbail will need to be a child of a StackPanel that sits inside the footer grid.
Thanks for your consideration and help.
I had a similar problem (clipping) with a WPF animation I had. The problem was that the owner of the animation needed to be a parent of both containers for the animation to work (in my case I made it the actual window holding the containers).
Without any code, I can't see if that is your problem, but I thought I would throw it out there.
You can see my code where I animate moving from one container to another here:
http://wiassistant.codeplex.com/sourcecontrol/changeset/view/36638?projectName=WIAssistant#924851
(See the AnimatePaneBox method at the bottom of the file.) This may or may not be useful to you.
I've done something similar by creating a Canvas that sits over the top of both containers, using a WritableBitmap (if necessary) to create a rendering of the object that you're moving and attach it to that Canvas, animate the bitmap (translate, scale, opacity, whatever), and then pop the new object in under it at the end of the animation. It can be brittle if your controls need to be able to move or resize, but in most of my circumstances it's been a reliable hack.

WPF and Silverlight controls and layouts pan and zoom capabilities

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?
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.
Use a MultiScaleImage or Canvas area, and place everything you need to pan and zoom in it
<Canvas x:Name="panZoomPanel" Background="Transparent">
</Canvas>
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
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.
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.

WinForm designer and right snapline for controls in container when resizing it

Using Visual Studio 2008 WinForm designer, I have a container (form, panel, groupbox, whatever) and some controls in it.
The container is set to not automatically resize or dock in any way. When placing a control in the container I can use the snaplines to help in positioning the control.
However, the snaplines does not appear when I resize the container to the edge of the contained controls...
Is there a simple way to have the designer show me the snaplines of the contained controls when resizing the container?
The designer will only show the snap lines when moving a child control within a container. You can demonstrate it by moving a GroupBox around a Form and see that it shows snap lines when the GroupBox gets close to the edge of the Form, but if you were to resize the Form you won't see the snap lines appear.
If you're just looking to get all of the controls to line up in a uniform fashion, I'd suggest switching to SnapToGrid mode and using the grid lines to align your controls. You can set the SnapToGrid mode by going to Tools->Options->Windows Forms Designer->LayoutMode. Open your designer and you should see the grid appear, after that you can line your controls up with the grid.
After setting the layout of the form, you can also set anchoring property to all child controls appropriately, to right and bottom (or top and left), depending on the resizing you are making. Anchor property will hold child controls to same distance from the edges of the container (parent control).
In the WinForms designer, there is no support for snaplines when resizing the container control. The best way to work around this issue is to first size the container to the size you would like to use and then add controls. Optionally, you can change the Margin property of the container so that when moving controls inside the container, they will snap to the margin of the container, keeping it uniform.
Try:
container.AutoSize = true
container.AutoSizeMode = GrowAndShrink
container.Padding.All = 5

WPF - how to best implement a panel with draggable/zoomable children?

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.
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
You can attach a 'Draggable' behavior to each element.

Resources