rectangle drawn in different point when canvas is zoomed - c#

I have a canvas which has a zooming function. There are a lot of elements inside of it so, for selection I was going to use a selection box which I create dynamically when selection box is clicked. On clicking the button, I add the rectangle to the canvas and again, on clicking the button, I remove it.
I have the following xaml code:
<Viewbox x:Name="vbCanvas">
<Grid x:Name="theGrid"
MouseDown="Grid_MouseDown"
MouseUp="Grid_MouseUp"
MouseMove="Grid_MouseMove"
Background="Transparent">
<Canvas Name="canvasWaSNA" Margin="0,10,10,10" Height="720" Width="1280">
</Canvas>
</Grid>
</Viewbox>
mouse events of theGrid draws the rectangle on runtime on the canvas. The codes for those events are:
bool mouseDown = false;
Point mouseDownPos;
Point mouseUpPos;
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{
mouseDown = true;
mouseDownPos = e.GetPosition(theGrid);
theGrid.CaptureMouse();
// Initial placement of the drag selection box.
Canvas.SetLeft(sBox, mouseDownPos.X);
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Width = 0;
sBox.Height = 0;
// Make the drag selection box visible.
sBox.Visibility = Visibility.Visible;
}
}
private void Grid_MouseUp(object sender, MouseButtonEventArgs e)
{
// Release the mouse capture and stop tracking it.
mouseDown = false;
mouseUpPos = e.GetPosition(theGrid);
theGrid.ReleaseMouseCapture();
// Show the drag selection box.
sBox.Visibility = Visibility.Visible;
MessageBox.Show(mouseDownPos.ToString() + " " + mouseUpPos.ToString());
}
private void Grid_MouseMove(object sender, MouseEventArgs e)
{
if (mouseDown)
{
// When the mouse is held down, reposition the drag selection box.
Point mousePos = e.GetPosition(theGrid);
if (mouseDownPos.X < mousePos.X)
{
Canvas.SetLeft(sBox, mouseDownPos.X);
sBox.Width = mousePos.X - mouseDownPos.X;
}
else
{
Canvas.SetLeft(sBox, mousePos.X);
sBox.Width = mouseDownPos.X - mousePos.X;
}
if (mouseDownPos.Y < mousePos.Y)
{
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Height = mousePos.Y - mouseDownPos.Y;
}
else
{
Canvas.SetTop(sBox, mousePos.Y);
sBox.Height = mouseDownPos.Y - mousePos.Y;
}
}
}
To create a Rectangle at runtime, I have to click a button. The event of that button is as follows:
private void select_Click_1(object sender, RoutedEventArgs e)
{
if (!canvasWaSNA.Children.Contains(sBox))
{
sBox.Name = "selectionBox";
sBox.StrokeThickness = 1.5 / zoomfactor;
sBox.StrokeDashArray = new DoubleCollection { 1, 2 };
sBox.Visibility = System.Windows.Visibility.Collapsed;
sBox.Stroke = Brushes.Gray;
canvasWaSNA.Children.Add(sBox);
}
else
{
sBox.Visibility = System.Windows.Visibility.Collapsed;
canvasWaSNA.Children.Remove(sBox);
}
}
I am using the following code to zoom into the canvas:
double zoomfactor = 1.0;
void window_MouseWheel(object sender, MouseWheelEventArgs e)
{
Point p = e.MouseDevice.GetPosition(canvasWaSNA); //gets the location of the canvas at which the mouse is pointed
Matrix m = canvasWaSNA.RenderTransform.Value;
if (e.Delta > 0)
{ //the amount of wheel of mouse changed. e.Delta holds int value.. +ve for uproll and -ve for downroll
m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
zoomfactor *= 1.1;
}
else
{
m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);
zoomfactor /= 1.1;
}
canvasWaSNA.RenderTransform = new MatrixTransform(m);
}
When my canvas is on original size, The rectangle is drawn perfectly but as I zoom in or zoom out, rectangle is drawn abnormally. It starts to draw from other points. What might be the problem? Please help

Well, I wasnot supposed to capture the mouse position with respect to theGrid, as I had to create the rectangle with respect to canvas. So, I have to get the position as e.GetPosition(canvasWaSNA) and the intended result was shown. It captured the mouse position on the canvas. Now, the rectangle is drawn perfectly even when zoomed in or zoomed out.
Also, I improved the StrokeThickness of the rectangle drawn by referencing it with the zoomfactor of the canvas.
private void Grid_MouseDown(object sender, MouseButtonEventArgs e)
{mouseDown = true;
mouseDownPos = e.GetPosition(canvasWaSNA);
theGrid.CaptureMouse();
sBox.StrokeThickness = 1.5 / zoomfactor;
// Initial placement of the drag selection box.
Canvas.SetLeft(sBox, mouseDownPos.X);
Canvas.SetTop(sBox, mouseDownPos.Y);
sBox.Width = 0;
sBox.Height = 0;
// Make the drag selection box visible.
sBox.Visibility = Visibility.Visible;
}

Related

How can I move a Picture with just "drag" the picture?

So I want to make a funtunality like in Windows when you display a Picture that when you zoom in you can just move the Picture with the mouse. I already got the zoom part in my project:
<ScrollViewer Name="scroll" Margin="40"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Canvas
MouseWheel="Container_MouseWheel" x:Name="canvas">
<Viewbox>
<Image x:Name="img" Source="{Binding imageSource}" Grid.ColumnSpan="2" />
</Viewbox>
</Canvas>
</ScrollViewer>
and the Code in the MouseWheel Event:
var element = sender as UIElement;
var position = e.GetPosition(element);
var transform = element.RenderTransform as MatrixTransform;
var matrix = transform.Matrix;
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1); // choose appropriate scaling factor
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
element.RenderTransform = new MatrixTransform(matrix);
but now Ive got the Problem that when I zoom I cant just click and hold the Image with the mouse and move to an another part of the picture, if you dont really know what I mean just open an Image on windows zoom in and then hold the mouse and move in the picture.
What I have tried was to add these 3 Events to my Canvas and this is the code behind for the 3 Events:
private Image draggedImage;
private Point mousePosition;
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var image = e.Source as Image;
if (image != null && canvas.CaptureMouse())
{
mousePosition = e.GetPosition(canvas);
draggedImage = image;
Panel.SetZIndex(draggedImage, 1); // in case of multiple images
}
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (draggedImage != null)
{
canvas.ReleaseMouseCapture();
Panel.SetZIndex(draggedImage, 0);
draggedImage = null;
}
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (draggedImage != null)
{
var position = e.GetPosition(canvas);
var offset = position - mousePosition;
mousePosition = position;
Canvas.SetLeft(draggedImage, Canvas.GetLeft(draggedImage) + offset.X);
Canvas.SetTop(draggedImage, Canvas.GetTop(draggedImage) + offset.Y);
}
}
but this didnt work saddly, does anyone has an Idea on how to make it work?
You should not mix layout (i.e. set Canvas.Left/Top) and render transformations. If you zoom by means of a RenderTransform, you should also pan by the same RenderTransform.
Here is a simple example where the MouseMove handler also manipulates the RenderTransform of the Image element.
<Canvas x:Name="canvas"
Background="Transparent"
MouseLeftButtonDown="CanvasMouseLeftButtonDown"
MouseLeftButtonUp="CanvasMouseLeftButtonUp"
MouseMove="CanvasMouseMove"
MouseWheel="CanvasMouseWheel">
<Image x:Name="image" Source=...>
<Image.RenderTransform>
<MatrixTransform/>
</Image.RenderTransform>
</Image>
</Canvas>
Note that mouse positions are determined differently for pan and zoom, in order to get an "unscaled" offset for panning, but an origin point relative to the Image for zooming.
private Point? mousePosition;
private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (canvas.CaptureMouse())
{
mousePosition = e.GetPosition(canvas); // position in Canvas
}
}
private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
canvas.ReleaseMouseCapture();
mousePosition = null;
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
if (mousePosition.HasValue)
{
var position = e.GetPosition(canvas); // position in Canvas
var translation = position - mousePosition.Value;
mousePosition = position;
var transform = (MatrixTransform)image.RenderTransform;
var matrix = transform.Matrix;
matrix.Translate(translation.X, translation.Y);
transform.Matrix = matrix;
}
}
private void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
{
var position = e.GetPosition(image); // position in Image
var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);
var transform = (MatrixTransform)image.RenderTransform;
var matrix = transform.Matrix;
matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
transform.Matrix = matrix;
}

MS Chart Control: prevent zoom when clicking

I'm using a MS Chart Control that sets a cursor when the chart is clicked and that enables the user to zoom in and out. When the user tries to click into the chart it accidentally happens that he drags a very small zoom rectangle and the chart zooms in instead of handling the click.
What can be done to prevent zooming in when trying to click? Is there something like a minimum rectangle size for zooming?
Here's how I handle the click:
_area = new ChartArea();
private void chart1_MouseClick(object sender, MouseEventArgs e)
{
try
{
_area.CursorX.SetCursorPixelPosition(new Point(e.X, e.Y), true);
}
catch (Exception ex)
{
}
}
And this is how I setup the zoom and cursor settings:
_area.AxisX.ScaleView.Zoomable = true;
_area.CursorX.IsUserSelectionEnabled = true;
_area.CursorX.IntervalType = DateTimeIntervalType.Seconds;
_area.CursorX.Interval = 1D;
_area.CursorY.IsUserSelectionEnabled = true;
_area.CursorY.Interval = 0;
You can manually handle zooming yourself. You can use the MouseDown event to capture the start X and start Y. Then use the MouseUp event to capture the end X and end Y. Once you have your start and end points you can determine if you want to zoom or not. If you want to zoom you can use the helper function below to manually zoom.
private void set_chart_zoom(ChartArea c, double xStart, double xEnd, double yStart, double yEnd)
{
c.AxisX.ScaleView.Zoom(xStart, xEnd);
c.AxisY.ScaleView.Zoom(yStart, yEnd);
}
Based on #Baddack's answer here's a complete solution. The key is to disable the zoom feature of the chart and zoom manually (like Baddack suggested) by using MouseUp/MouseDown events. The user selection feature of the chart is kept enabled to use the selection rectangle for setting the zoom interval.
This sample code checks if the zoom retangle is at least 10 pixels wide and high. Only if that's the case the zoom is initiated:
private ChartArea _area;
private Point _chartMouseDownLocation;
...
private void MainForm_Load(object sender, EventArgs e)
{
...
// Disable zooming by chart control because zoom is initiated by MouseUp event
_area.AxisX.ScaleView.Zoomable = false;
_area.AxisY.ScaleView.Zoomable = false;
// Enable user selection to get the interval/rectangle of the selection for
// determining the interval for zooming
_area.CursorX.IsUserSelectionEnabled = true;
_area.CursorX.IntervalType = DateTimeIntervalType.Seconds;
_area.CursorX.Interval = 1D;
_area.CursorY.IsUserSelectionEnabled = true;
_area.CursorY.Interval = 0;
}
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
_chartMouseDownLocation = e.Location;
}
private void chart1_MouseUp(object sender, MouseEventArgs e)
{
// Check if rectangle has at least 10 pixels with and hright
if (Math.Abs(e.Location.X - _chartMouseDownLocation.X) > 10 &&
Math.Abs(e.Location.Y - _chartMouseDownLocation.Y) > 10)
{
// Zoom to the Selection rectangle
_area.AxisX.ScaleView.Zoom(
Math.Min(_area.CursorX.SelectionStart, _area.CursorX.SelectionEnd),
Math.Max(_area.CursorX.SelectionStart, _area.CursorX.SelectionEnd)
);
_area.AxisY.ScaleView.Zoom(
Math.Min(_area.CursorY.SelectionStart, _area.CursorY.SelectionEnd),
Math.Max(_area.CursorY.SelectionStart, _area.CursorY.SelectionEnd)
);
}
// Reset/hide the selection rectangle
_area.CursorX.SetSelectionPosition(0D, 0D);
_area.CursorY.SetSelectionPosition(0D, 0D);
}

Mouse_move event is fired but updateChart function inside of it is called only once. Why?

I have issue with windows form. Working on mouse move event which gets fired when mouse moves over chart and attempts to scroll the chart. When user click on chart it will call mouseDown event and grab point clicked. I am using this clicked point along with e.X(current point when mouse moves) in mouse move event to check if mouse is moving right or left. I can see change in values when mouse moves but that change is applied only once. i.e. When mouse is moved chart should scroll along with mouse. but all I get is just one shift in chart when I lift the mouse up. It does not scroll when mouse is moving instead it changes value when mouse mouse moves up.
Following is the code for mouse move and mouse up event.
Variable that I declared to use in following functions
bool isMousepressed = false; //used to relate mouse event with zoom buttons
bool isZoomed = false;
bool isMoving = false; //allow scrolling of chart after zooming in
bool isMouseDown = false; //used to relate mouse events occured in zoomed in chart
int pointClicked;
MouseDown
private void chart_MouseDown(object sender, MouseEventArgs e)
{
isMouseDown = true;
//if mouse is clicked after first zoom in it will scroll chart
if (isZoomed)
{
this.Cursor = csrHand;
pointClicked = e.X;
isMoving = true;
return;
}
//******when left click is made and we have some points in beginSelection
//******i.e. start point for zoom, then only you show the rectangle selection frame
if (e.Button == System.Windows.Forms.MouseButtons.Left && !isMousepressed)
{
//do something
isMousepressed = true;
}
else
{
//do something
}
}
Mouse up event
private void chart_MouseMove(object sender, MouseEventArgs e)
{
if (!isMouseDown) return;
//checking for zoom
if (isZoomed && isMoving)
{
double offset = 0;
//checks if mouse moved to left side after clicking --> scroll chart from right to left --> adding points at the end of chart
if (e.X < pointClicked)
{
double xmax = lastmaxT;
xmax = xmax + (0.20 * xmax);
offset = xmax - lastmaxT;
if (!isMouseEventOccured) { xStart = lastminT; }
//after increasing value of xmax if it exceeds max x value then s
if (xmax >= maxNumChartPointZoomOut) { isMoving = false; return; }
updateChart(lastSelectedProfile, lastSelectedWeldIndex, (xStart + offset), xmax);
}
//checks if mouse moved to right side after clicking --> scroll chart from left to right --> adding points at the beginning
if (e.X > pointClicked)
{
//lastminT is xStart value after zoom in
double xmin = lastminT;
xmin = xmin - (0.20 * xmin);
offset = lastminT - lastminT;
if (!isMouseEventOccured) { xEnd = chartA.BottomMax; }
//if after decreasing value of
if (xmin <= 0) { isMoving = false; return; }
updateChart(lastSelectedProfile, lastSelectedWeldIndex, xmin, (xEnd - offset));
}
return;
}
//checks if left mouse button is clicked or not to capture beginning point and then end point for drawing selection rectangle
if (e.Button == System.Windows.Forms.MouseButtons.Left && beginSelectionPoint != Point.Empty)
{
//do something
}
else
{
//do something
}
}
here is the code for mouse up event
private void chart_MouseUp(object sender, MouseEventArgs e)
{
if (isZoomed)
{
this.Cursor = System.Windows.Forms.Cursors.Default;
isMouseDown = false;
return;
}
Point p1, p2;
//when mouse up occurs it first checks, with isMousePressed, if mouse was pressed or not
if (isMousepressed)
{
isMousepressed = false;
isMouseEventOccured = true;
isMouseDown = false;
//do something... here I get the value of xStart and xEnd
if (!isZoomed) { isZoomed = true; }
//call updatechart() with start and end points on graph being p1/p2 or beginselection/endselection
updateChart(lastSelectedProfile, lastSelectedWeldIndex, xStart, xEnd);
lastminT = xStart;
lastmaxT = xEnd;
beginSelectionPoint = endSelectionPoint = Point.Empty;
}
else return;
}
As per my search and understanding mouse move event fires when mouse is moved over given control and executes code continuously (including calling of any function like I have updateChart()). Any explanation about mouse move event is appreciated if my understanding sounds wrong from mouse move event.
Note: my chart is not using MSChart and no DataVisualization package. I am new to development so I will try to provide as much info as I can to solve and any help is much appreciated. Thank you

How to drag and drop a colored circle into a canvas in WPF?

I have created an ellipse object which I can fill through a color combobox.
Now I need to be able to click(and hold) the circle, drag my cursor to a canvas and when I release my mousebutton a new circle should be drawn at that place, with the same dimensions and color as the circle I clicked.
I think I'm doing something wrong in the Drop event.
private Ellipse dragCircle = new Ellipse();
private void eBallColor_MouseMove(object sender, MouseEventArgs e)
{
dragCircle = (Ellipse)sender;
if (e.LeftButton == MouseButtonState.Pressed)
{
DataObject dragColor = new DataObject("theColor", dragCircle.Fill);
DragDrop.DoDragDrop(dragCircle, dragColor, DragDropEffects.Move);
}
}
private void Ellipse_Drop(object sender, DragEventArgs e)
{
Ellipse circle = (Ellipse)sender;
if (e.Data.GetDataPresent("theColor"))
{
Brush draggedColor = (Brush)e.Data.GetData("theColor");
circle.Fill = draggedColor;
dragCircle.Fill = Brushes.White;
Point pos = e.GetPosition(canvasCard);
double posX = pos.X;
double posY = pos.Y;
Canvas.SetLeft(dragCircle, posX);
Canvas.SetTop(dragCircle, posY);
canvasCard.Children.Add(dragCircle);
}
}

Zoom Image in Picture Box

I am creating an application, in which user will be able to Upload an image and then Zoom in and Out the image on a particular location (current mouse pointer).
Also user should be able to drag the image to see other parts of the image when the image is zoomed.
I have implemented some functionality to achieve it but i am scaling the complete image. I want to know that how i can scale a particular portion of image, or scale the complete image and then point to that location where my current mouse pointer is placed.
Code:
private void DisplayIsdDiagram(BO.IsdDiagram IsdDiagram)
{
DoubleBuffered = true;
zoomFac = 1;
translateX = 0;
translateY = 0;
transStartX = 0f;
transStartY = 0f;
picIsdDiagram.BorderStyle = BorderStyle.Fixed3D;
bmp = new Bitmap(Image.FromStream(new MemoryStream(IsdDiagram.Image.ToArray())));
if (bmp.Width > bmp.Height)
{
ratio = (float)picIsdDiagram.Width / (float)bmp.Width;
translateRatio = (float)bmp.Width / (float)picIsdDiagram.Width;
}
else
{
ratio = (float)picIsdDiagram.Height / (float)bmp.Height;
translateRatio = (float)bmp.Height / (float)picIsdDiagram.Height;
}
//picIsdDiagram.Image = bmp;
picIsdDiagram.Refresh();
picIsdDiagram.MouseWheel += new MouseEventHandler(picIsdDiagram_MouseWheel);
}
private void picIsdDiagram_MouseWheel(object sender, MouseEventArgs e)
{
IsZooming = true;
if (e.Delta < 0)
{
if (zoomFac > 1)
zoomFac = zoomFac - (float)0.1;
}
else
{
if (zoomFac <= 5)
zoomFac = zoomFac + (float)0.1;
}
picIsdDiagram.Refresh();
IsZooming = false;
}
private void picIsdDiagram_MouseDown(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = true;
transStartX = e.X;
transStartY = e.Y;
}
private void picIsdDiagram_MouseUp(object sender, MouseEventArgs e)
{
IsZooming = false;
IsMouseDown = false;
translateX = translateX + ((e.X - transStartX) * (translateRatio / zoomFac));
translateY = translateY + ((e.Y - transStartY) * (translateRatio / zoomFac));
picIsdDiagram.Refresh();
}
private void picIsdDiagram_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.ScaleTransform(ratio * zoomFac, ratio * zoomFac);
if (IsZooming == false && IsMouseDown == true)
g.TranslateTransform(translateX, translateY);
g.DrawImage(bmp, 0, 0);
}
I tried to get the current mouse position from MouseHover event and tried to Translate the picture when only Zoom is done, but that is not working.
Also the Picture Box has some other multiple Picture boxes inside it, to show some representation on the big image. While scaling the Big Image, small images (inside images) should not be scaled. Although there position needs to be recalculated to show them at their real places even after zooming on the Big image.
So in above i am facing two problems:
1) To Zoom an Image at any particular location (current mouse pointer)
by scrolling.
2) To regenerate the coordinates of the sub images while
zooming and translating.
Any help that can guide me in correct direction.
Also if by any other means, i could be able to achieve this functionality.
Application : Windows
Control : Picture Box (Please suggest if any other control can be used, if not possible with this)
Language : C#
Waiting for response!
PictureEdit control provided by DevExpress 13.2

Categories