I have added the scroll bar to the x-axis of my mschart control using this link Adding a scroll bar to MS Chart control C# and it worked as expected. But now my requirement is, I need zooming for both the axis. But since I removed Zoom reset button for x-axis, I have used the following to reset it by forcing.
private void chart1_AxisScrollBarClicked(object sender, ScrollBarEventArgs e)
{
// Handle zoom reset button
if(e.ButtonType == ScrollBarButtonType.ZoomReset)
{
// Event is handled, no more processing required
e.IsHandled = true;
// Reset zoom on X and Y axis
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
}
}
But it is not working properly. Please help me in fixing this in c#..
Try using ZoomReset(0).
private void zeroZoom_Click(object sender, EventArgs e)
{
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset(0);
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset(0);
}
The first thing coming in mind, is that your problem is related to multiple zooming.
As you had noticed, by default the zoom-reset button (exactly like the ZoomReset method) doesn't reset the zoom completely, but restore the previous view-status, i.e. if you have zoomed more than one time, it returns just to the previous zoomed view.
To completely reset the zoom, you can use this code:
while (chart1.ChartAreas[0].AxisX.ScaleView.IsZoomed)
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
while (chart1.ChartAreas[0].AxisY.ScaleView.IsZoomed)
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
Conversely, if you like the default zoom-reset behaviour, you should have two buttons for the two axis because it's possible to have different number of view-statex for the different axis.
Another possibility, is that you are zooming a secondary axis, like AxisX2 or AxisY2 (not sure, but I think that is depending on the chart type), so you should reset those (or, to be safe, just reset all axis...).
I tried with the below code today and it seems like working fine. Here the for loop handles the X axis with scroll and the next if block handles the ordinary X-axis. Could you please have a glance at it and let me know your views about it?
private void chart1_AxisScrollBarClicked(object sender, ScrollBarEventArgs e)
{
Boolean blnIsXaxisReset = false;
try
{
// Handle zoom reset button
if(e.ButtonType == ScrollBarButtonType.ZoomReset)
{
// Event is handled, no more processing required
e.IsHandled = true;
// Reset zoom on Y axis
while (chart1.ChartAreas[0].AxisY.ScaleView.IsZoomed)
chart1.ChartAreas[0].AxisY.ScaleView.ZoomReset();
//Handles Zoom reset on X axis with scroll bar
foreach (Series series in chart1.Series)
{
if (series.YAxisType == AxisType.Secondary)
{
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(-10, 10);
blnIsXaxisReset = true;
break;
}
}
//Handles Zoom reset on ordinary X axis
if (blnIsXaxisReset == false)
{
while (chart1.ChartAreas[0].AxisX.ScaleView.IsZoomed)
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset();
}
}
}
catch (Exception ex)
{
BuildException buildException = new BuildException();
buildException.SystemException = ex;
buildException.CustomMessage = "Error in zooming the Chart";
ExceptionHandler.HandleException(buildException);
}
}
Thanks for your effort!!
Related
How do i make Live Charts graph to show the last update after changing zoom
https://i.stack.imgur.com/fECsZ.png
I face the same problem after zooming and panning. My way is to let the user reset the zoom and panning when they are done looking at the point they want. I did it in code behind when the reset button is clicked.
private void btn_Reset_Click(object sender, RoutedEventArgs e)
{
foreach (var XAxis in ConstantChangeCartesianChart.AxisX)
{
XAxis.MinValue = double.NaN;
XAxis.SetRange(double.NaN, double.NaN);
}
foreach (var YAxis in ConstantChangeCartesianChart.AxisX)
{
YAxis.MinValue = double.NaN;
YAxis.SetRange(double.NaN, double.NaN);
}
}
This will simply reset all the range and minimum value. Hope this help.
I'm writing a graphing program, and I want to be able to hover my mouse over a point on the graph to reveal details about the point. I'm aware of MouseHover(), but that only works when hovering over the form, not specific points on the form. I've written a function that works, but is incredibly resource intensive and not very reliable using MouseHover:
List<List<Point>> Points { get; set; } = new();
private async void GraphPanel_MouseHover(object sender, EventArgs e)
{
await Task.Run(() => {
do
{
foreach(var pointList in Points)
{
foreach (Point point in pointList)
{
if (Control.MousePosition == point)
{
throw new Exception();
}
}
}
}
while (true);
});
}
Is there a more practical way of doing this?
MouseHover is called a lot of times. You can:
Save mouse position into a variable in MouseHover. Save the currentTime (DateTime.UtcNow) in other variable.
Use Task.Delay to wait, for example, 100 ms. In the code executed after this delay, check if current mouse position is the same (o closest if you want work with some tolerance). Then, the mouse was 100 ms in the same position: do your work. If not, do nothing.
Avoid create a new Task if (DateTime.UtcNow-YourPreviouslySavedDate).TotalMilliSeconds is less than 100 ms
Another option is using Timer component. Set the interval and attach a method for the event (double click in the component). On the event, check mouse position and do your work.
I've started using LightningChart in my real time monitoring application. In my app there are many y axis which use segmented layout (one y axis per segment):
mainChart.ViewXY.AxisLayout.YAxesLayout = YAxesLayout.Segmented;
My goal is that when you mouse click a segment, it gets larger compared to other segments (kinda like zoom effect) and the other segments gets smaller. When you click it again it goes back to normal.
I know I can change the size of the segments with:
mainChart.ViewXY.AxisLayout.Segments[segmentNumber].Height = someValue;
That takes care of the zooming effect.
Now the problem is that how can I solve which segment was actually clicked? I figured out that you get mouse position via MouseClick -event (e.MousePos) but that seem to give only the screen coordinates so i'm not sure that it helps.
I'm using the LightningChart version 8.4.2
You are correct that getting mouse position via MouseClick event is the key here. The screen coordinates you get via e.GetPosition (not e.MousePos) can be converted to chart axis values with CoordToValue() -method. Then you just compare the y-coordinate to each y-axis minimum/maximum value to find out what segment was clicked. Here is an example:
_chart.MouseClick += _chart_MouseClick;
private void _chart_MouseClick(object sender, MouseButtonEventArgs e)
{
var mousePos = e.GetPosition(_chart).Y;
double axisPos = 0;
bool isWithinYRange = false;
foreach (AxisY ay in _chart.ViewXY.YAxes)
{
ay.CoordToValue((float)mousePos, out axisPos, true);
if (axisPos >= ay.Minimum && axisPos <= ay.Maximum)
{
// Segment clicked, get the index via ay.SegmentIndex;
isWithinYRange = true;
}
}
if (!isWithinYRange)
{
// Not in any segment
}
}
After finding out the segment index, you can modify its height as you described:
_chart.ViewXY.AxisLayout.Segments[0].Height = 1.5;
Note Height means segment height compared to other segments.
Hope this is helpful.
Is there any way I can click on a MSChart and drag it left/right in order to scroll the chart to the right/left.
The idea is to drag the chart instead of dragging the chart's scroll bar.
The reason I'm not simply using the scroll bar is because I was asked to add this feature (bosses and their demands..).
Btw, I'm using C# and winforms.
Hope I was clear enough.
Thanks!!
Yes this is possible.
One issue is that you can't use the normal zooming/scrolling mechanism as this itself works by dragging over the chart.
Instead pass it by and code two mouseevents and use the Axis.PixelPositionToValue function to access the data values.
First we store the data value of the clicked location:
double mDown = double.NaN;
private void chart1_MouseDown(object sender, MouseEventArgs e)
{
Axis ax = chart1.ChartAreas[0].AxisX;
mDown = ax.PixelPositionToValue(e.Location.X);
}
Then we calculate the old range how far the Axis.Minimum has moved and adapt Minimum and Maximum:
private void chart1_MouseMove(object sender, MouseEventArgs e)
{
if (!e.Button.HasFlag(MouseButtons.Left)) return;
Axis ax = chart1.ChartAreas[0].AxisX;
double range = ax.Maximum - ax.Minimum;
double xv = ax.PixelPositionToValue(e.Location.X);
ax.Minimum -= xv - mDown;
ax.Maximum = ax.Minimum + range;
}
You will want to add a few checks to prevent dragging too far left or right..
Note that the code assumes that you have set Minimum and Maximum!
Also note that you will want to take control over the format of the x-axis labels so they don't make the chart jump around by having lot of fractional digits.
Or, depending on your data, you may want to set the step to a multiple of some value, suitable for your interval or in my case simply to ints:
ax.Minimum -= (int)(xv - mDown) ;
If you need to have the scrollbars visible you can still use the above code; but you need to replace this
ax.Minimum -= (int)(xv - mDown);
ax.Maximum = ax.Minimum + range;
by this:
double oldPos = ax.ScaleView.Position;
ax.ScaleView.Position -= (xv - mDown);
However this does interfer with the normal scolling. You can insert checks to make sure you are not hitting the scrollbar like this:
RectangleF ipar = InnerPlotPositionClientRectangle(chart1, chart1.ChartAreas[0]);
if (ipar.Contains(e.Location))
{
..
..
..but it still acts up here. So either or seems to work best.
The InnerPlotPositionClientRectangle is defined here.
I'm using the C# built-in Winforms Chart control (System.Windows.Forms.DataVisualization.Charting.Chart) with its built-in ability to let the user select a range. What I'd like to do is to read back what range the user has selected. Surely there must be some easy way to do this, but I haven't been able to find it.
The cursor is enabled like so:
var ca = chart1.ChartAreas["ChartArea1"].CursorX;
ca.CursorX.IsUserEnabled = true;
ca.CursorX.IsUserSelectionEnabled = true;
I am aware that I can make the chart zoom when the user selects a range by enabling ca.AxisX.ScaleView.Zoomable, but I don't want the picture to change: instead I am using the chart as a way to display information and let the user select a range of X values for which I then do some extra processing.
I tried hooking to chart1.SelectionRangeChanged and that indeed fires every time the range is changed - I just can't seem to get the selection range from the CursorEventArg I get back. It has "NewSelectionStart" and "NewSelectionEnd" fields, but those are NaN, disappointingly. I tried looking at the various properties of the chart and the axes, but didn't find anything that sounded promising.
Further investigation reveals the ChartArea.CursorX.SelectionStart property which sounds like exactly what I need... except that it's NaN too. I don't know whether this is normal or I'm hitting some sort of bug?
So, how can I figure out what range the user selected?
OK, well, I figured it out. Here's the scoop:
There's a SelectionRangeChang*ing* event, and when that one runs the ChartArea.CursorX.SelectionStart and ChartArea.CursorX.SelectionEnd fields have correct values in them. But the user hasn't released the mouse button yet, so you should just store them.
When the user releases the mouse button, the SelectionRangeChang*ed* event fires. Somehow it's designed in such a way that SelectionStart and SelectionEnd are reset to NaN (just like the NewSelectionStart and NewSelectionEnd fields in the event parameters). What you have to do is to use the values that you squirreled away from the other event handler now that you know the time is right to use them.
So there you have it! Hopefully this answer will save someone else from wasting time.
In addition to redtuna to set cursors in a c# chart:
It worked for me to use "SelectionRangeChanging" instead of "SelectionRangeChanged" to not get the NaN issue:
When initializing the Form
this.chart1.SelectionRangeChanging += chart1_SelectionRangeChanging;
and
chart1.ChartAreas[0].CursorX.IsUserEnabled = false; // red cursor at SelectionEnd
chart1.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = false; // zoom into SelectedRange
chart1.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;
chart1.ChartAreas[0].CursorX.Interval = 0.01; // set "resolution" of CursorX
What is executed if the range is chosen / the cursors are set
private void chart1_SelectionRangeChanging(object sender, CursorEventArgs e)
{
double x1 = x1 = e.NewSelectionStart; // or: chart1.ChartAreas[0].CursorX.SelectionStart;
double x2 = e.NewSelectionEnd; // or: x2 = chart1.ChartAreas[0].CursorX.SelectionEnd;
double diffx1x2 = x2 - x1;
}
To zoom in & out (x-axis) I just added a button that takes the cursor values. That way zooming by mouseClick (ScaleView.Zoomable = false;) does not interfere my cursor positioning :)
private void button_ZoomIn(object sender, EventArgs e)
{
double x1 = chart1.ChartAreas[0].CursorX.SelectionStart; // x1 = X1
double x2 = chart1.ChartAreas[0].CursorX.SelectionEnd; // x2 = X2
if (x2 > x1)
{
// hard setting: chart1.ChartAreas[0].AxisX.Minimum = x1;
// hard setting: chart1.ChartAreas[0].AxisX.Maximum = x2;
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(x1,x2); // dynamic approach with scrollbar
}
else
{
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(x2,x1);
}
}
Zoom out
private void button_ZoomOut(object sender, EventArgs e)
{
chart1.ChartAreas[0].AxisX.ScaleView.ZoomReset(0);
}
Zooming can also be implemented by mouseWheel: how to enable zooming in Microsoft chart control by using Mouse wheel
And if you also want right-click action in the chart: How to get a right click mouse event? Changing EventArgs to MouseEventArgs causes an error in Form1Designer?