I have been struggling for the past week in transformation of image within the limits of canvas
void image_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var name = (Image)sender;
var translate = (CompositeTransform)name.RenderTransform;
var newPosX = Canvas.GetLeft(name) + translate.TranslateX + e.Delta.Translation.X;
var newPosY = Canvas.GetTop(name) + translate.TranslateY + e.Delta.Translation.Y;
if (!isBoundary(newPosX, DrawCanvas.ActualWidth - name.ActualWidth, 0))
translate.TranslateX += e.Delta.Translation.X;
if (!isBoundary(newPosY, DrawCanvas.ActualHeight - name.ActualHeight, 0))
translate.TranslateY += e.Delta.Translation.Y;
}
bool isBoundary(double value, double max, double min)
{
return value > max ? true : value < min ? true : false;
}
This is my dragging code it works fine and also isBoundary function limits the image inside canvas inly but when I am scaling with this code
{
var translate = (CompositeTransform)name.RenderTransform;
translate.CenterX = name.ActualWidth / 2;
translate.CenterY = name.ActualHeight / 2;
translate.ScaleX += e.Delta.Translation.X*0.001 ;
translate.ScaleY += e.Delta.Translation.Y*0.001;
}
Scale is also done but not limiting inside canvas and I when I scale my image to make big its canvas is also scaling and when I am shriking in size its canvas (drag) area is also shrinking.
Need help in limiting scaling and its canvas problem.
Related
I am using a transparent image, from this package
https://www.codeproject.com/Articles/25048/How-to-Use-Transparent-Images-and-Labels-in-Window
I am loading the image at the start and everything is fine.
To the left of the transparent image, I am loading a normal PictureBox.
The picturebox then starts moving towards another control that is to the right of the transparent image, meaning that the picturebox passes above the transparent image (which is intended).
When the picturebox moves past the transparent image, the part that the picturebox went over is "erased".
Can anyone help keep the transparent image not erased?
Animating the control function:
public static void SlideToDestination(Control destination, Control control, int delay, Action onFinish)
{
control.BringToFront();
new Task(() =>
{
int curX = control.Left + (control.Width / 2);
int curY = control.Bottom - (control.Height / 2);
int desX = destination.Left + (destination.Width / 2);
int desY = destination.Bottom - (destination.Height / 2);
int directionX = desX > curX ? 1 : -1;
int directionY = desY > curY ? 1 : -1;
while (curX != desX || curY != desY)
{
try
{
if (curX != desX)
{
control.Invoke((Action)delegate ()
{
control.Left += directionX;
});
}
if (curY != desY)
{
control.Invoke((Action)delegate ()
{
control.Top += directionY;
});
}
Thread.Sleep(delay);
}
catch
{
// form could be disposed
break;
}
curX = control.Left + (control.Width / 2);
curY = control.Bottom - (control.Height / 2);
}
if (onFinish != null) onFinish();
}).Start();
}
Before Image:
After Image:
I figured out the solution. In the SlideToDestination function, after changing the location, all I needed to do was call Form1.snake.Update() where snake is the transparent image control. Then it redraws the snake every time after updating the location.
I have a custom written chart control. My charts are displaying time on x-axis and current on y-axis. What I want is, selected zoom. So when user clicks and drag mouse over chart it shows selection rectangle and selects particular area on chart and updates the chart to display points of only selected area.
My problem is, I can zoom in for the first time and it shows me correct results on updated chart. with correctly updated x coordinates. But when I do it again (anytime after 1st zoom in) points are always off.
All I want is when user selects another area on already zoom in chart it should do further zoom in with updated x coordinates.
What I tried: From proper observation of my chart zoom in behavior I found that every time when I click on chart that point is mapped to my original chart width and not updated width(updated width I found as (endingXPoints - beginingXPoints) when I do zoom). So I have to map points according to updated width. But That does not work. I still do not get correct zoom in. Also I tried using scaling factor as chart size is changing but still result is not correct.
So my question how to map the points of zoomed chart on my original chart. I am doing this in windows form and using custom written library.
I just began my career as developer and still in learning process. Any suggestion will be great help to me. Thank you
Here is my mouse_up event, if that code helps. I have try to add as many comments as possible. In this code I am saving previous points clicked on stack and using it to get scale factor and get the scaled
private void chart_MouseUp(object sender, MouseEventArgs e)
{
Point p1, p2;
//when mouse up occurs it first checks, with isMousePressed, if mouse was pressed or not
if (isMousepressed)
{
isMousepressed = false;
isMouseEventOccured = true;
int xLeft = chartA.GetLeftDistToXaxis;//left distance from starting of control to x axis
int xWidth = chartA.chartWidth;//actual x-axis width shown on control
int yBottom = chartA.yTopPadding + chartA.chartHeight;//bottom distance from starting of control to y axis
//if no start - end points are selected then do nothing and return
if (endSelectionPoint == Point.Empty || beginSelectionPoint == Point.Empty) return;
//if start point is same as end point, do noting and return and if selection is made on left side of Y axis do nothing
if (endSelectionPoint.X == beginSelectionPoint.X) return;
if (endSelectionPoint.Y == beginSelectionPoint.Y) return;
if (beginSelectionPoint.X < xLeft && endSelectionPoint.X < xLeft) return;//avoid left chart area
if (beginSelectionPoint.Y > yBottom && endSelectionPoint.Y > yBottom) return;//avoid bottom chart area
endSelectionPoint.X = e.X;
endSelectionPoint.Y = e.Y;
//when all of the above conditions are false, we have two different start/end points which is not empty. map it & draw rectangle
p1 = ((Control)sender).PointToScreen(beginSelectionPoint);
p2 = ((Control)sender).PointToScreen(endSelectionPoint);
//draw selection rectangle
ControlPaint.DrawReversibleFrame(chartA.getRectangleForPoints(p1, p2), Color.Black, FrameStyle.Dashed);
//checking the begin and end value of x-y coordinates to see if they are on chart. if not then set the boundaries
//check it for begin coordinates
if (beginSelectionPoint.X < xLeft) { beginSelectionPoint.X = xLeft; }
if (beginSelectionPoint.X > (xLeft + chartA.Width)) { return; /*beginSelectionPoint.X = (xLeft + chartA.Width);*/ }
//if (beginSelectionPoint.Y < yBottom) { beginSelectionPoint.Y = yBottom; }
//if (beginSelectionPoint.Y > (yBottom + chartA.Height)) { beginSelectionPoint.Y = (yBottom + chartA.Height); }
//check it for end coordinates
if (endSelectionPoint.X > (xLeft + chartA.Width)) { endSelectionPoint.X = (xLeft + chartA.Width); }
if (endSelectionPoint.X < xLeft) { endSelectionPoint.X = xLeft; }
//if (endSelectionPoint.Y < yBottom) { endSelectionPoint.Y = yBottom; }
//if (endSelectionPoint.Y > (yBottom + chartA.Height)) { endSelectionPoint.Y = (yBottom + chartA.Height); }
//actual x-y value on chart.....x->corresponding time; y-> corresponding Amperage
xStart = 10 * chartA.MouseToXProportion(beginSelectionPoint.X );//multiplied with 10 to get the correct x values, if not used values in 0.+
yStart = chartA.MouseToYProportion(beginSelectionPoint.Y);
xEnd = 10 * chartA.MouseToXProportion(endSelectionPoint.X);
yEnd = chartA.MouseToYProportion(endSelectionPoint.Y);
if (zoomStack.Count != 0)
{
Point prevZoomPtEnd = (Point)zoomStack.Pop();
Point prevZoomPtStart = (Point)zoomStack.Pop();
double oldWidth = prevZoomPtEnd.X - prevZoomPtStart.X;
double zoomedWidth = endSelectionPoint.X - beginSelectionPoint.X;
double scaleFactor = zoomedWidth / oldWidth;
xStart = 10 * (chartA.MouseToXProportion(beginSelectionPoint.X) * scaleFactor);
xEnd = 10 * (chartA.MouseToXProportion(endSelectionPoint.X) * scaleFactor);
zoomStack.Push(beginSelectionPoint);
zoomStack.Push(endSelectionPoint);
}
else
{
zoomStack.Push(beginSelectionPoint);
zoomStack.Push(endSelectionPoint);
}
double xTemp;
if (xStart > xEnd) { xTemp = xEnd; xEnd = xStart; xStart = xTemp; }
//call updatechart() with start and end points on graph being p1/p2 or beginselection/endselection
updateChart(lastSelectedProfile, lastSelectedWeldIndex, xStart, xEnd);
}
else return;
}
Do you have to map the points? The chart's built in zoom should handle zooming correctly without trying to re-invent the wheel. The code below should be enough, hold left click and drag to zoom into the range.
private void setUserSelection(Chart cht)
{
cht.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
cht.ChartAreas[0].CursorX.IsUserEnabled = true;
cht.ChartAreas[0].CursorX.LineColor = Color.Transparent;
cht.ChartAreas[0].CursorX.SelectionColor = Color.Lime;
cht.ChartAreas[0].CursorX.Interval = 0;
cht.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
cht.ChartAreas[0].AxisX2.ScaleView.Zoomable = true;
cht.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
cht.ChartAreas[0].CursorY.IsUserEnabled = true;
cht.ChartAreas[0].CursorY.LineColor = Color.Transparent;
cht.ChartAreas[0].CursorY.SelectionColor = Color.Lime;
cht.ChartAreas[0].CursorY.Interval = 0;
cht.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
cht.ChartAreas[0].AxisY2.ScaleView.Zoomable = true;
}
To zoom out back one zoom, you'll need to add the following code somewhere. In my example below I have mine on the mouse right click.
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Right)
{
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset(1);
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset(1);
}
}
I want to drag multiple items on canvas for this I use the following link
this
it is dragging but it went out of canvas I want that to restrict its position inside canvas only . What offset aur values to +/- ??
Thanks.
You have to add clipping area to the canvas
by default clipping value is null. (No clipping)
canvas.Clip = new RectangleGeometry();
canvas.Clip.Rect = new Rect(0, 0, canvas.ActualWidth, canvas.ActualHeight);
...
if you lost your control because you include ManipulationModes.TranslateInertia ( use ManipulationMode.All ) and it hard to control when you swipe dragableItem fast , try set your ManipulationMode to this
DragableItem.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY;
...
for checking boundary before making translation
void DragableItem_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
{
var translate = (TranslateTransform)DragableItem.RenderTransform;
var newPosX = Canvas.GetLeft(DragableItem) + translate.X + e.Delta.Translation.X;
var newPosY = Canvas.GetTop(DragableItem) + translate.Y + e.Delta.Translation.Y;
if( ! isBoundary(newPosX,parentCanvas.ActualWidth - DragableItem.ActualWidth,0) )
translate.X += e.Delta.Translation.X;
if( !isBoundary(newPosY,parentCanvas.ActualHeight - DragableItem.ActualHeight,0))
translate.Y += e.Delta.Translation.Y;
}
bool isBoundary(double value,double max,double min)
{
return value > max ? true : value < min ? true : false;
}
I'm making instrument to select part of image. I have PictrureBox, and simple way to make it :
void StartPanel(object sender, MouseEventArgs args)
{
xStart = args.X;
yStart = args.Y;
panelStarted = true;
pan.Location = new Point(xStart, yStart);
}
void FinishPanel(object sender, MouseEventArgs args)
{
xFinish = args.X;
yFinish = args.Y;
panelStarted = false;
}
void UpdatePanel(object sender, MouseEventArgs args)
{
if (panelStarted)
{
int x = args.X;
int y = args.Y;
int newxstart = xStart;
int newystart = yStart;
int neww = 0;
int newh = 0;
if (x >= xStart)
neww = x - xStart;
else
{
neww = xStart - x;
newxstart = x;
}
if (y >= yStart)
newh = y - yStart;
else
{
newh = yStart - y;
newystart = y;
}
pan.Size = new Size(neww, newh);
pan.Location = new Point(newxstart, newystart);
}
}
When I move mouse right and down, it is absolutely ok. But when I move it left or up I can see blinks at my area. So I have understood, that it is because when I move mouse left or up, my panel is redrawed, because Panel.Location is changed, and when I move mouse right and down, location is not changed, only size is changed, so it is not redrawed, just some pixels are added to panel. What is standart solution for this?
It's not easy trying to see what you are trying to do, but I guess you are using a panel as a draggable control to drag over the picturebox surface capturing the portion of image below (like a lens) - yes?
If so, then this is not the best way to do it. It is better to just draw a rectangle on the picturebox surface and "drag" that around - this is simple with just using the mouse events to sets the top left corner and use the onpaint to draw the unfilled rectangle over the image. Capturing the image when you are ready is simple too using whatever event you wish, then copy the image giving the same positions to the new bitmap.
Putting one control over another often causes flickers - even with double buffering. It also takes far more code.
Since you are describing a drawing issue when resizing the panel, probably the easiest fix is to replace the panel you are using with one that is double buffered and will invalidate on resize a event:
public class BufferedPanel : Panel {
public BufferedPanel() {
this.DoubleBuffered = true;
this.ResizeRedraw = true;
}
}
I have a custom control that zooms on a custom drawn document canvas.
I tried using AutoScroll but it was not giving satisfactory results. When I would set AutoScrollPosition and AutoScrollMinSize back to back (in any order) it would force a paint and cause jitter each time the zoom changes. I assume this was because it was calling an Update and not Invalidate when I modified both properties.
I am now manually setting the HorizontalScroll and VerticalScroll properties with AutoScroll set to false like so each time the Zoom level or the client size changes:
int canvasWidth = (int)Math.Ceiling(Image.Width * Zoom) + PageMargins.Horizontal;
int canvasHeight = (int)Math.Ceiling(Image.Height * Zoom) + PageMargins.Vertical;
HorizontalScroll.Maximum = canvasWidth;
HorizontalScroll.LargeChange = ClientSize.Width;
VerticalScroll.Maximum = canvasHeight;
VerticalScroll.LargeChange = ClientSize.Height;
if (canvasWidth > ClientSize.Width)
{
HorizontalScroll.Visible = true;
}
else
{
HorizontalScroll.Visible = false;
HorizontalScroll.Value = 0;
}
if (canvasHeight > ClientSize.Height)
{
VerticalScroll.Visible = true;
}
else
{
VerticalScroll.Visible = false;
VerticalScroll.Value = 0;
}
int focusX = (int)Math.Floor((FocusPoint.X * Zoom) + PageMargins.Left);
int focusY = (int)Math.Floor((FocusPoint.Y * Zoom) + PageMargins.Top);
focusX = focusX - ClientSize.Width / 2;
focusY = focusY - ClientSize.Height / 2;
if (focusX < 0)
focusX = 0;
if (focusX > canvasWidth - ClientSize.Width)
focusX = canvasWidth - ClientSize.Width;
if (focusY < 0)
focusY = 0;
if (focusY > canvasHeight - ClientSize.Height)
focusY = canvasHeight - ClientSize.Height;
if (HorizontalScroll.Visible)
HorizontalScroll.Value = focusX;
if (VerticalScroll.Visible)
VerticalScroll.Value = focusY;
In this case, FocusPoint is a PointF structure that holds the coordinates in the bitmap which the user is focused on (for example, when they mouse wheel to zoom in they are focusing on the current mouse location at that time). This functionality works for the most part.
What does not work is the scroll bars. If the user tries to manually scroll by clicking on either scroll bar, they both keep returning to 0. I do not set them anywhere else in my code. I have tried writing the following in the OnScroll() method:
if (se.ScrollOrientation == ScrollOrientation.VerticalScroll)
{
VerticalScroll.Value = se.NewValue;
}
else
{
HorizontalScroll.Value = se.NewValue;
}
Invalidate();
But this causes some very erratic behavior including flicking and scrolling out of bounds.
How am I supposed to write the code for OnScroll? I've tried the base.OnScroll but it didn't do anything while AutoScroll is set to false.
I ended up implementing my own custom scrolling by creating 3 child controls: an HScrollBar, a VScrollBar, and a Panel.
I hide ClientSize and ClientRectangle like so:
public new Rectangle ClientRectangle
{
get
{
return new Rectangle(new Point(0, 0), ClientSize);
}
}
public new Size ClientSize
{
get
{
return new Size(
base.ClientSize.Width - VScrollBar.Width,
base.ClientSize.Height - HScrollBar.Height
);
}
}
The layout is done in OnClientSizeChanged:
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
HScrollBar.Location = new Point(0, base.ClientSize.Height - HScrollBar.Height);
HScrollBar.Width = base.ClientSize.Width - VScrollBar.Width;
VScrollBar.Location = new Point(base.ClientSize.Width - VScrollBar.Width, 0);
VScrollBar.Height = base.ClientSize.Height - HScrollBar.Height;
cornerPanel.Size = new Size(VScrollBar.Width, HScrollBar.Height);
cornerPanel.Location = new Point(base.ClientSize.Width - cornerPanel.Width, base.ClientSize.Height - cornerPanel.Height);
}
Each ScrollBar has their Scroll event subscribed to the following:
private void ScrollBar_Scroll(object sender, ScrollEventArgs e)
{
OnScroll(e);
}
And finally we can allow MouseWheel events to scroll with the following:
protected override void OnMouseWheel(MouseEventArgs e)
{
int xOldValue = VScrollBar.Value;
if (e.Delta > 0)
{
VScrollBar.Value = (int)Math.Max(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), 0);
OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
}
else
{
VScrollBar.Value = (int)Math.Min(VScrollBar.Value - (VScrollBar.SmallChange * e.Delta), VScrollBar.Maximum - (VScrollBar.LargeChange - 1));
OnScroll(new ScrollEventArgs(ScrollEventType.ThumbPosition, xOldValue, VScrollBar.Value, ScrollOrientation.VerticalScroll));
}
}
For custom painting, you would use the following statement:
e.Graphics.TranslateTransform(-HScrollBar.Value, -VScrollBar.Value);
This worked perfectly without the glitches present when using AutoScroll.