I've only been working with WPF for a few months, but I have an extensive WinForm, ASP.NET, and Flex background. I am attempting to draw a user control which looks like the following IMAGE.
We are using the MVVM pattern. When the user control first loads, everything draws correctly. The control consists of a canvas. Within the canvas exists a radial panel which will geometrically place UIElements which are added. The circular nodes are drawn into the radial panel. The PolyLines shown are drawn in the canvas. I get the end points of the PolyLine by using a GeneralTransform and a call to UIElement.TransformToVisual. Where the UIElement in the call is the blue node in the radial panel and it is transforming with the radial panel. When the control is loaded the first time, everything does draw correctly. Where it fails is when the MVVM pattern informs there is an update to the drawing.
When I attempt to refresh based off the system update, I have confirmed all of my collections contain the proper data. So the MVVM pattern is behaving like it should. However, when I attempt to draw the lines, my call to TransformToVisual is returning a point of 0,0 instead of the value it does during the load. This causes my polylines to draw at the top left of the control instead of connecting the two nodes.
Here is the order of operations:
1) User Control load will build my collections used to draw the control. These collections are built local to the User Control and the data comes from a master collection of info which resides in the view model. The user control registers a refresh method in itself to a UI refresh broadcast message.
2) The user attempts to connect two blue nodes through a wizard. The connector is saved and the refresh UI message is broadcast.
3) The User Control invokes the Refresh method. Here I am looping the children of the canvas and removing the PolyLines. I am also calling radialPanel.Children.Clear. I then perform the same code which built my collections at load. I then invalidate my user control to invoke the OnRender. The OnRender will then attempt to draw the Polylines to connect the nodes. The nodes are always placed properly in the radial panel.
4) The line connecting action is called from the OnRender. Within it I am using the GeneralTransform gt = node.TransformToVisual(radialPanel). This call always returns 0,0 when the user control is "refreshed" but always works the first load for the control.
I'm fairly positive the issue lies with how I refresh the user control based on the network notification. I've tried to clear the controls, then re-do the same load operations, but still no luck. I've executed InvalidateVisual against the user control and tried to do everything in my overriden OnRender still no luck; 0,0 is still returned.
Does anyone have any ideas on what could be happening to the GeneralTransform? I'm at a dead end and any new path would help.
I figured it out. So I'm posting the answer in case anyone else deals with this issue.
The problem was I wasn't forcing MeasureOverride and ArrangeOverride of the radial panel before attempting to draw the lines. Without these two called, the nodes are at position 0,0 at the time I connect my lines. By forcing them to call before drawing the lines, the TransformToVisual works properly.
Related
I'm faced with a problem: I am trying to automate a control with UI Automation. The control is a viewer in a client application, which hooks into a service hosted remotely. As a result of some legacy design decisions, this viewer simply displays a bitmap on a canvas. When interactions occur (e.g. clicks), the position of the click is sent to the service, which uses the co-ordinates to work out where the click occurred, and react correspondingly. The result of this is a nightmare for UI test automation. There is no way of hooking directly into sub-controls, because they are simply painted on to the bitmap. I have found a back-end way of accessing information about what is in this canvas, but now I need to work out where, in this scrolling bitmap, those items appear, so I can interact with them. I use positional information based on the upper part of the control, but since I don't have access to the bounding rects of these sub-controls, as soon as I scroll, all this information becomes invalid. My main problem is that I can't work out how far the scroll bar moves the canvas. Since scroll bars in UIA only have values from 0-100 (despite the actual magnitude of the scrolling effect), I can't work out how far down the canvas I've moved from a known position (it would depend on how far the scroll bar can move at that given time - i.e. how many sub-controls have been rendered in the bitmap). Is there any way of working out the magnitude of the scroll event on this canvas? I know that this must be done internally - the scroll bar has to know how far to move the canvas, based on the actual size of the canvas. However, the bounding rect of the canvas only gives it's visible on-screen position - it doesn't indicate how big the underlying image is. I either need to get the full size of the bitmap (as if it was rendered fully on screen, without scrolling), or to know how much adjusting the scroll value effects the visible position of the image. Is there any way of working this out?
I have a set of custom controls and a list of geometric objects that I need to draw on the same handle.
I override the OnPaint procedure, put the base.OnPaint(e) in the first line and commence with the drawing of the geometric objects (via e.Graphics.DrawLine etc.).
Those are in a locked list and decay after a while.
Also the customized controls can move around the window.
Now this is almost working as intended except for this fun fact:
The geometric shapes appear only after a control is moved along/above their layouts.
I was able to reproduce this in a small environment: PASTEBIN
I tried flushing the graphics object; save/restore; changing Clip.
Nothing seemed to work.
I am guessing that regions only get invalidated and repainted once a control is present. But how can I force them to be drawn anyways?
Control.Invalidate will tell the framework that the control needs to be redrawn. It does this automatically to redraw controls after, for example, the mouse has obscured part of it, but it will only redraw a the small section that the mouse covered (hence why you end up with a "(re)painting with the mouse" effect. Also moving a window will force it to redraw, as does covering it with another window and then moving that window away again.
But if you are doing a bunch of custom painting, you need to let it know that the control needs to be redrawn by calling Invalidate yourself.
I'm developing a small tool where you can drag and drop controls with the mouse, such as buttons, comboboxes, groupboxes, etc...
As the mouse moves, the control moves too, so the user can get a preview of the position of that control. The problem comes when, the control being dragged is going from one container to another one. Usually resulting in that control moving correctly but just under the second container, and therefore, not visible for the user. Let's say that we have GroupBox A and GroupBox B, and the user wants to move Button from A to B. When the action starts, the button is visible while the movement is inside the bounds of A, but when the mouse gets into B, the Buttons just appears to be in a lower layer of the interface and it becomes invisible.
I've tried to fix it with .BringToFront() and .SendToBack() without results. I'm having some flickering problems also trying to create a bitmap to create that preview even using the methods recomended here to solve it.
Is there a way of fixing it without having to use bitmaps that solves the layer problem?
I have created a custom panel that renders like a canvas renders it's children. I would like that 2 elements that hit each other (ie. touch each other somewhere) get replaced by another control that allows custom rendering of these elements near each other and for example puts them in a context menu.
I have code that can correctly detect these hits, however, I cannot seem to replace the elements by other elements, as the panel is always drawing the items in InternalChildren array.
I cannot do this hit testing before they get to the panel. As they come from a view that can be grouped and due to zooming in and out (which effects the positions/sizes of the children), won't know which elements collide until measuring of the panel happens.
Is there any way to let the panel render other elements than those that are in the InternalChildren array?
EDIT: image
http://www.imagedump.nl/img827/2849/12unled.png
Note that I do not care if block C get's replaced by another component or not, so if replacing it with a merged block is easier (which I think it will be), do so.
I need to create a form with two panels:
1. Destination
2. Source
On the source panel there will be picture boxes. I need to be able to move it from source to point at destination panel with mouse.
I have a problem connected with different coordinates of the panels.
Please, help with advice or an idea what to do.
Moving those controls requires changing their Parent property. That's not easy to do, there is no good time to do this while the user is dragging with the mouse. You'll also get the effect of the panel clipping the control, you cannot display it on both with half of the control on one and the other half on the other panel. And yes, you have to change the Location property of the control when you change the parent or it will jump.
Punt the problem, don't use two panels. It only has to look like a panel, easily done by drawing one in the form's Paint method (or OnPaint override, better). Use e.Graphics.DrawRectangle or FillRectangle.