Recover previous absolute position for PictureBox when Parent is changed? - c#

In my GUI, i need to change Parents of PictureBoxes to the background PictureBox so that the transparency works correctly.
However, changing the parent also changes the location of the pictureboxes. I have tried grabbing the absolute location of the picture box via the PointToClient, but it doesn't work. I put the coordinates in the comments, and they don't change after assigning the new parent even though the image visibly changes location. Furthermore, I don't expect that it could possibly work as it's being passed a point, not an object with more information about parents and whatnot that's needed to deduce the absolute position.
What is the correct way to deduce the absolute position of an element so that I can move the image to the correct location after its parent changes? Or is there a better way to do this?
Point oldRel = pictureBox4.Location; //258, 109
Point oldAbs = PointToClient(oldRel); //75, -96
//Commenting out this line fixes the image shift but ruins the transparency
pictureBox4.Parent = pictureBox2;
Point newRel = pictureBox4.Location; //258, 109
Point newAbs = PointToClient(pictureBox4.Location); //75, -96

This will move a Control child from one Parent to a new one, keeping the absolute screen position intact:
void MoveTo(Control child, Control newParent )
{
child.Location = newParent.PointToClient(child.PointToScreen(Point.Empty));
child.Parent = newParent;
}
The trick with PointToClient and PointToScreen is to use them from the right parent control; not setting the control will default to the Form, which will miss out on the actual position of the parent..

Related

Winforms SplitterPanel, z-index of child overlap split

I am working with a SplitterPanel in winforms where on the right hand side I want a custom dropdown list control which displays a 2 columns dropdown list.
The problem is that with there being two columns I want to be able to have a larger dropdown list area than the actual dropdown, and therefore overlap the SplitterPanel if the list doesn't fit in the split area.
I have tried using .BringToFront();, however this does not work on the SplitterPanel and the control is hidden. I come from a web background where I would have used z-index for this but I am stumped with winforms. See below image of my issue.
Does anyone know how I can resolve this?
The z-index will determine which child controls sit higher and can overlap which others child controls. But it never helps when you want a (real as opposed to drop downs or menues) child overlap its own container. This never happens; and since the CheckedListBox is the child of the split panel it will never overlap it.
You will need to make the CheckedListBox sit not inside the splitter panel but in its Parent so the they are 'brethren'. Let's assume the SplitContainer sits in a TabPage tabPage8. Then you can show it fully by moving it to that tabPage:
moveCtlToTarget(tabPage8, checkedListBox1);
using this function.
void moveCtlToTarget(Control target, Control ctl)
{
// get screen location of the control
Point pt = ctl.PointToScreen(Point.Empty);
// move to the same location relative to the target
ctl.Location = target.PointToClient(pt);
// add to the new controls collection
ctl.Parent = target;
// move all up
ctl.BringToFront();
}
As I don't know how you create and show it, resetting it is up to you. Note that as it now is no longer in the split panel it will not move when you move the splitter..
You may want to do this only the first time and later align it with the ComboBox..
TaW's answer above helped my solve my issue. I modified it slightly for my situation where I moved the parameters into the method as I already had my checkbox control set as a property of the control and got the target by looping up the parents until I got to the top.
private void moveCtlToTarget()
{
Control Target = Parent;
while (Target.Parent != null)
Target = Target.Parent;
Point pt = CheckBox.PointToScreen(Point.Empty);
CheckBox.Location = Target.PointToClient(pt);
CheckBox.Parent = Target;
CheckBox.BringToFront();
}

Changing stroke color dynamically

I am using stylus input to draw lines in a canvas. I want to change the color of the stroke with the pen pressure. so I used:
DrawingAttributes dattribute = new DrawingAttributes();
inkcan.EditingMode = InkCanvasEditingMode.Ink;
if (stylusInput.pressureFactor < 0.5)
dattribute.Color = Colors.Red;
else
dattribute.Color = Colors.Blue;
inkcan.DefaultDrawingAttributes = dattribute;
but I have found that the color changes only when I lift off and retouch the pen to tablet surface. I am not sure how to fix that problem.
Any help would be greatly appreciated.
Look at this question: InkCanvas Eraser
In the MSDN it states:
If you change the EraserShape, the cursor rendered on the InkCanvas is
not updated until the next EditingMode change.
The effect you are experiencing might be caused by the EditingMode being changed when you pull the pen off the canvas and put it back down.
If so, you could toggle the EditingMode property as I suggested in the linked answer.
EDIT
Have a look at this a 3rd down it says:
Off course, life is never as simple as that, so there is one more
little issue to handle. Apparently, the InkCanvas uses different
renderers for the final result compared to while the strokes are being
drawn. To show a transparency based on the pressure while the
drawing action is still in progress, we need to use the protected
property called DyamicRenderer which gets/sets the object used to
render the strokes on a drawing context while the stroke is being
drawn. This rendering object must be a descendent of DynamicRenderer.
All you need to do here is override the OnDraw method and change the
brush that is used. When you assign a new value to this property, the
InkCanvas actually changes an internal 'PlugIn list' which is called
whenever data is entered using the stylus.
This might be it.
The if condition is evaluated only once, so there is no reason for the colour to change while you are drawing. Unfortunately, there seems to be no "onpressurechanged" event, so you would probably have to set up a loop that checks the pressure every x milliseconds and adjusts the colour accordingly.
Since you don't want to freeze the UI, you would either need to run it in another thread and delegate the colour change back to the UI thread, or you can queue the pressure checks onto the window dispatcher with "applicationIdle" priority. This would look something like:
void checkPressure(InkCanvas inkcan)
{
//return if touch is lifted code here
DrawingAttributes dattribute = new DrawingAttributes();
if (stylusInput.pressureFactor < 0.5)
dattribute.Color = Colors.Red;
else
dattribute.Color = Colors.Blue;
inkcan.DefaultDrawingAttributes = dattribute;
this.Dispatcher.BeginInvoke(new MyPressureDelegate(checkPressure), DispatcherPriority.ApplicationIdle, inkcan);
}
assuming your stylusInput shares scope with the function, of course. Otherwise you'd need to pass it in along with the canvas in an object array.

WPF: Get the real 'logical' position of Child within its parent Panel

Another problem in my WPF questions series :)
I'm creating custom Decorator that will be used to decorate Panels (or it can be Behavior, no difference).
That Decorator deals with elements that are in that Panel (Children property of the Panel). It attaches some RenderTransforms to those elements. Now I need a position of some element ('child' of the Panel) relative to the Panel itself. In other words I need a position if some child element in the Panels coordinate space. in just another words I want the offset that was specified by the ArrangeOverride method of the Panel when calling Arrange method on Children.
That seem to be easy. But I cannot find the way to always get right coordinates.
This code
VisualTreeHelper.GetOffset(child)
does not work when the panel is inside ScrollView - it takes topmost, leftmost visible corner of the Panel as an origin of coordinate space - not the real topmost and leftmost corner of the Panel.
The code
Point position = child.TransformToAncestor(panel).Transform(new Point(0,0));
will not work when some render transforms are already active on the child element of the Panel. It will return the position of transformed image(by the render transfrom) of child element. The render position.
The same problem is with this aproach:
Point panelPosition = panel.PointToScreen(new Point(0, 0));
Point childPosition = child.PointToScreen(new Point(0, 0));
Point position = new Point(childPosition.X - panelPosition.X, childsPosition.Y - panelPosition.Y);
So this i what I have tryied but it did not work. I have 2 similar questions on this topic that were tying to simplify the problem, so I got some of suggestions above. Now I introduced the problem in its full complexity, I hope to get the right advice.
If something is unclear please leave the comment.
Thank you
You can "undo" the RenderTransform before calling TransformToAncestor:
Point origin = child.RenderTransform.Inverse.Transform(new Point(0, 0));
Point position = child.TransformToAncestor(listBox).Transform(origin);

Getting the top left coordinates of a WPF UIElement

I am working on extending the Microsoft resize Adorner example and need to be able to reposition the element after say the bottom left drag handle has been dragged.
So if I have a textbox of say 150 wide, 35 high postitioned on my form, and the bottom left drag handle changes the width to 200 wide, the right hand of the text box remains unchanged but the left hand edge moves to the left.
So I need to know the top left coordinates of the UIElement. I have tried Canvas.GetLeft and Canvas.GetTop but they return NaN which is confusing.
I just tried VisualTreeHelper.GetOffset which does return an offset but when you try and use it in the arrange method of the element it disappears, presumably as the values in the offset are too high.
In the days before Wpf the coordinate system was quite simple, wpf has overcomplicated things I think.
And if someone just wants the control's screen coordinates:
Point targetLoc = targetCtrl.PointToScreen(new Point(0, 0));
(this doesn't match the thread's description, but it does match the title. Figured it might help people coming in off search results)
You can transform coordinates of the UIElement to its parent. In your case it's a form. Here is an example of a method that returns coordinates of a visual:
private Point GetPosition(Visual element) {
var positionTransform = element.TransformToAncestor(MyForm);
var areaPosition = positionTransform.Transform(new Point(0, 0));
return areaPosition;
}

How do i get the mouse position?

-edit- I dont need the answer anymore now that i calculate based on mousedown position.
Basically in relation to this question It seems like e.X,y and e.Location.X,Y is relative to mousedown? I seen negative values when i havent moved it left/above the parent picbox.
So it seems like the values are relative to my mousedown, but i need values relative to the parent because i update the left/top every event and other datat and relative to mousedown is breaking my code.
Did you try to do this ( assuming your control is named picbox )
Point pt = picbox.PointToClient(point)

Categories