MS Chart Control: prevent zoom when clicking - c#

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);
}

Related

C# GUI -- Make Shape Follow Mouse and Place On Click

I'm developing a basic GUI Windows Form Application. It allows the user to change colors using sliding bars, change the size of shapes using scroll bars, and click a check box to determine if the shape should be filled, clear the screen if the clear button is clicked, and switch between shapes with buttons. I'm currently using the MouseMove event to cause the shape to follow the mouse around inside of the panel. I also have an event for a MouseClick. When there is a mouseclick, I want it to "place" that shape so that the graphic persists. I've been able to move a single shape to where the user clicks and make it follow the mouse, I just don't know how to combine both.
This is what I have so far:
void DrawShape()
{
//g.Clear(Color.White);
p.Color = Color.FromArgb(r, gr, b);
if (IsChecked) // If checkbox is checked for fill
{
if (IsRect)
{
g.FillRectangle(myBrush, left, top, wid, ht); // Filled Rectangle
}
else if (IsEllipse)
{
g.FillEllipse(myBrush, left, top, wid, ht); // Filled Ellipse
}
}
else // not filled
{
if (IsRect)
{
g.DrawRectangle(p, left, top, wid, ht); // empty rectangle
}
else if (IsEllipse)// empty ellipse
{
g.DrawEllipse(p, left, top, wid, ht);
}
}
}
private void panel1_MouseMove(object sender, MouseEventArgs e) // when mouse moves
{
left = e.X;
top = e.Y;
DrawShape();
}
private void panel1_MouseClick(object sender, MouseEventArgs e) //when mouse clicks
{
left = e.X;
top = e.Y;
IsClick = true;
DrawShape();
IsClick = false;
}

Zooming with mouse wheel breaks panning (scrolling) of real time data in ZedGraph

Windows Forms ZedGraph control in WPF application. The data points are auto-generated and attached to the chart every N seconds. When new data point is added to the chart I shift (pan) chart one point to the left, so there is always no more than last 50 points visible in the viewport. Overall, it works pretty good, except for two things.
Issues
If user tries to zoom in or out, the viewport stops following the last data point and chart goes outside of the screen, so panning stops working
I would like to pan or shift chart using mouse event, without scrolling, but when I press right mouse button and try to move it to the left, it tries to zoom chart instead of panning
protected void CreateChart(ZedGraphControl control)
{
_rand = new Random();
var curve = control.GraphPane.AddJapaneseCandleStick("Demo", new StockPointList());
curve.Stick.IsAutoSize = true;
curve.Stick.Color = Color.Blue;
control.AutoScroll = true; // Always shift to the last data point when new data comes in
control.IsEnableHPan = true; // I assume this should allow me to move chart to the left using mouse
control.IsEnableVPan = true;
control.IsEnableHZoom = true;
control.IsEnableVZoom = true;
control.IsShowPointValues = true;
control.IsShowHScrollBar = false;
control.IsShowVScrollBar = false;
control.IsAutoScrollRange = true; // Always shift to the last data point when new data comes in
control.IsZoomOnMouseCenter = false;
control.GraphPane.XAxis.Type = AxisType.DateAsOrdinal;
control.AxisChange();
control.Invalidate();
var aTimer = new Timer();
aTimer.Elapsed += new ElapsedEventHandler(OnTime);
aTimer.Interval = 100;
aTimer.Enabled = true;
}
protected XDate _xDate = new XDate(2006, 2, 1);
protected double _open = 50.0;
protected Random _rand = new Random();
// Auto generate data points
protected void OnTime(object source, ElapsedEventArgs e)
{
var control = FormCharts;
var x = _xDate.XLDate;
var close = _open + _rand.NextDouble() * 10.0 - 5.0;
var hi = Math.Max(_open, close) + _rand.NextDouble() * 5.0;
var low = Math.Min(_open, close) - _rand.NextDouble() * 5.0;
var pt = new StockPt(x, hi, low, _open, close, 100000);
_open = close;
_xDate.AddDays(1.0);
if (XDate.XLDateToDayOfWeek(_xDate.XLDate) == 6)
{
_xDate.AddDays(2.0);
}
(control.GraphPane.CurveList[0].Points as StockPointList).Add(pt);
control.GraphPane.XAxis.Scale.Min = control.GraphPane.XAxis.Scale.Max - 50; // Hide all points except last 50, after mouse zooming this line stops working
//control.GraphPane.XAxis.Scale.Max = control.GraphPane.XAxis.Scale.Max + 1;
control.AxisChange();
control.Invalidate();
}
Kind of solved it.
First issue with broken auto-scroll after zooming
It happens because zooming sets these parameters to FALSE.
area.XAxis.Scale.MinAuto = false;
area.XAxis.Scale.MaxAuto = false;
To fix it, either set it back to TRUE every time new data point comes. Another way to fix it, is to keep them always as FALSE and move chart manually
protected void MoveChart(GraphPane pane, int pointsToMove, int pointsToShow)
{
pane.XAxis.Scale.Max = pane.XAxis.Scale.Max - pointsToMove;
pane.XAxis.Scale.Min = pane.XAxis.Scale.Max - Math.Abs(pointsToShow);
}
...
// Example : shift one point to the left and show only 50 last points
MoveChart(control.MasterPane.PaneList["Quotes"], -1, 50);
Second issue, implementing custom panning without scrollbar using mouse events.
protected int _mouseX = -1;
protected int _mouseY = -1;
...
control.MouseUpEvent += OnMouseUp;
control.MouseDownEvent += OnMouseDown;
control.MouseMoveEvent += OnMouseMove;
...
// Example : remember X and Y on mouse down and move chart until mouse up event
protected bool OnMouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
_mouseX = -1; // unset X on mouse up
_mouseY = -1;
return true;
}
protected bool OnMouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
_mouseX = e.X; // remember last X on mouse down
_mouseY = e.Y;
return true;
}
protected bool OnMouseMove(ZedGraphControl sender, System.Windows.Forms.MouseEventArgs e)
{
if (_mouseX >= 0) // if X was saved after mouse down
{
foreach (var pane in sender.MasterPane.PaneList) // move synced chart panels
{
MoveChart(pane, _mouseX > e.X ? -1 : 1, 50); // if mouse move is increasing X, move chart to the right, and vice versa
}
_mouseX = e.X; // update X to new position
_mouseY = e.Y;
}
return true;
}

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

rectangle drawn in different point when canvas is zoomed

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

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