How do you have a panel `ClipToBounds` without clipping children? - wpf

I have a custom Panel implementation that renders objects relative to a physical space (think like a floor plan). The panel allows the following actions:
Zoom in/out
Pan up/down/left/right
and more that isn't relevant to this question
The panel lives in with several other elements on screen, and I need to make sure the custom panel's graphics don't spill over the navigation and other controls.
The problem is this:
If the panel is set to clip, it clips the children before arranging them.
Let's say I have a circle in the floor plan and the user zooms in enough to make the circle bigger than the parent control. The panel applies clipping to the circle as if it were placed dead center, then arranges the circle where it is supposed to be. The end result is that the circle no longer looks like a circle and I have gaps in the image.
I need the clipping to be applied after arranging the elements, or only applied to the overall image as compiled by the children. How can I do this?

It turns out I had the opposite problem of the question the OP had in his letter to Dr. WPF where ClipToBounds = "Maybe"
If I override the GetLayoutClip() method in my control and return null, I essentially turn off clipping for the element. However, since ClipToBounds is still true for my panel, the rendered item doesn't spill over the rest of the application.
In the base class for all items that will be added to my custom panel I have this:
protected override Geometry GetLayoutClip(Size layoutSlotSize)
{
return null;
}
In my case this is exactly what I want. I honestly want my layout clip to be based on the parent panel and not the child size (with no sense of location within the panel).

Related

WPF ScrollViewer set ScrollableHeight

I have a user control which paints content in OnRender. The Height of that control grows by and by.
I added this control to a ScrollViewer. The control does only repaint the currently visible area (viewport) (+/- a view lines for smoother scrolling).
Everything works fine so far...
But since the control usualy grows up to a few hundred thousands of pixels I want to keep the Height of my control as small as possible and provide a different Height value to bind to the ScrollableHeight of the ScrollViewer (same goes for VerticalOffset). But there is no setter for ScrollableHeight. It binds automatically to the Height of my Control. Neither can I override Height.
How can I customize my ScrollViewer (or VerticalScrollbar) to keep the real Height of my control small?
I did something like this in the past. What you need to do is to write your own layout Panel and implement the IScrollInfo in it. The interface looks big, but most of it is just calling one of the main set methods. The layouter needs to set some of the IScrollInfo properties, like ExtentHeight, Offset etc. and these are your way to customize how the ScrollViewer will calculate the scroll position and the scrollable area for your "virtual" canvas size. For implementing the IScrollInfo i used this tutorial as a guidance.

Relationship between a canvas in silverlight and rectangle

I am new to silver light and would like to understand a bit more from the pros. Let me tell you what I am trying to do. I am into photography and my goal is to create a web site that allows users to view their images and be able to rotate, zoom, crop, special effect etc. I have developed the web site that allows users to order pictures but now I want to start working on the actual picture/image manipulation. So for testing i put a canvas and a rectangle( with an image). Placed a slider and was able to link the slider to the rectangle. As i increase the slider the image gets larger. But I was kind of hoping as the image gets larger it does not surpass the boundries of the canvas. I assumed that is what it means by being a child of a canvas. Am i mistaken? If so how do you suggest me doing this? Remember I am very new to this and may be going about this very wrong.
Thanks!
Your are right. In Silverlight (like in WPF, WinForms etc.) gui-elements form a hierarchy of elements wherein controls can act either as parents or as children.
The reason why your rectangle surpasses the boundaries of it's container lies in the way controls are getting aligned. This depends on what kind of container you want to place your child into.
In a canvas for example you position the children with absolute measurements (left, top, height, width). In a self-organizing container like the StackPanel you choose an horizontal alignment (Left, HCenter, HStretch, Right) or a vertical alignment (Top, VCenter, VStretch, Bottom) which determines the childs behavior when you place it inside the parent. Furthermore you can specify the dimension of the child (Width, Height) and an optional margin which determines the gap between the Top, Right, Bottom and Left bound of your child to its enclosing parent.
But what ever container you choose it's inherent to it that you can let its children surpass its boundaries e.g. with a margin that is negative or greater than the container boundaries or simply by an child that is bigger in dimension that its container as you described the situation with your rectangle.
In your case I would consider working with the idea of clipping. Clipping simply means to
(1) define an geometrical area (in Silverlight and WPF it is a Path object) which lies over some graphical context (some section of your ui or your control etc).
(2) what lies inside the boundaries of this clipping area remains visible and everything else remains invisible.
So you can think of a clipping area as a window onto your screen which you use to look through.
When you are using Microsoft Blend this is easy to realize:
(1) Just use a geometrical shape like a Rectangle, a Circle or a custom Path.
(2) Place it somewhere upon your UI
(3) Right-click the shape, select "Path" and then "Make clipping Path"
(4) and voulĂ , you've just defined a clipping area which you can modify as you are used to modify controls.
Hope this gave you an idea how to deal with your problem.
cheers.

Panel with percentage coordinates

I would like use a panel whose children have coordinates specified as percentage of total panel's width/height. Moreover, I should be able to animate the coordinate property, for example to make a button move from 10% to 50% panel's width.
I've made 2 attempts:
Use a Grid and specify size as stars - this was not enough, because AFAIK by default WPF cannot animate distance properties specified by stars. I've found somewhere a custom class that enabled me to do so, it even worked, hovewer I consider that solution overly complicated an I am looking for something simpler.
Use a Canvas with fixed width and height and put it inside a Viewbox - this is a simple solution, but when resizing the Viewbox the whole content of Canvas is resized too. I want the content to have fixed size.
Is there a simple solution or should I implement my own panel (or maybe extend one of the existing ones, i.e. Canvas)?
Cheers!
I would:
subclass Canvas, perhaps calling it RelativeCanvas or RatioCanvas
add two attached properties: XRatio and YRatio
override ArrangeOverride and loop over all children. For each child, use their XRatio and YRatio along with the ActualWidth and ActualHeight of the RelativeCanvas to calculate and apply values for their Canvas.Left and Canvas.Top attached properties
You would use it as follows:
<local:RelativeCanvas>
<!-- the top-left of this button will be center of panel -->
<Button local:RelativeCanvas.XRatio="50" local:RelativeCanvas.YRatio="50"/>
</local:RelativeCanvas>
One thing you might like to add after you get that working is control over alignment. For example, I might to align the center of a control to the specified ratio, not its top-left corner.
There's one here: WPF Proportional Panel

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 - 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