I'm using a chart control within a C# windows forms project. What I would like is to have dotted lines follow my mouse around as it moves about the chart. I may make the lines centered about the cursor or the data point; at this point I am flexible. I've included a screen shot of what it is I'm looking for below.
So here you can see black dotted lines (the cursor doesn't appear because it was a screen grab). I already have a mouseMove event but I'm not sure which code to include in that mousemove to get this working (right now it only works when I click the mouse, but I think taht is just because I have CursorX.IsUserSelection enabled). I already formatted the lines in the chart creation function but is there some CursorX.LineEnable function or something similar? I've not been able to find any. I know that I could accomplish this with a painted object but I was hoping to avoid the hassle.
Thanks in advance! I'll include my line formatting below. This is in the chart creation section.
chData.ChartAreas[0].CursorX.IsUserEnabled = true;
chData.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
chData.ChartAreas[0].CursorY.IsUserEnabled = true;
chData.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
chData.ChartAreas[0].CursorX.Interval = 0;
chData.ChartAreas[0].CursorY.Interval = 0;
chData.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chData.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
chData.ChartAreas[0].CursorX.LineColor = Color.Black;
chData.ChartAreas[0].CursorX.LineWidth = 1;
chData.ChartAreas[0].CursorX.LineDashStyle = ChartDashStyle.Dot;
chData.ChartAreas[0].CursorX.Interval = 0;
chData.ChartAreas[0].CursorY.LineColor = Color.Black;
chData.ChartAreas[0].CursorY.LineWidth = 1;
chData.ChartAreas[0].CursorY.LineDashStyle = ChartDashStyle.Dot;
chData.ChartAreas[0].CursorY.Interval = 0;
Inside the chart's MouseMove event handler, you can do the following to make the cursor move:
private void chData_MouseMove(object sender, MouseEventArgs e)
{
Point mousePoint = new Point(e.X, e.Y);
Chart.ChartAreas[0].CursorX.SetCursorPixelPosition(mousePoint, true);
Chart.ChartAreas[0].CursorY.SetCursorPixelPosition(mousePoint, true);
// ...
}
This is the documentation for the SetCursorPixelPosition method: http://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.cursor.setcursorpixelposition.aspx
Related
Here is my init method for the chart in my C# program.
private void initGraph()
{
chartTrend.Cursor = Cursors.Hand;
chartTrend.ChartAreas[0].CursorX.LineColor = Color.Red;
chartTrend.ChartAreas[0].CursorX.LineWidth = 2;
chartTrend.ChartAreas[0].CursorX.LineDashStyle = ChartDashStyle.Dot;
chartTrend.ChartAreas[0].CursorX.IsUserEnabled = true;
// let us select a portion of chart so then zoom that portion
chartTrend.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
chartTrend.ChartAreas[0].CursorX.Interval =1 ;
chartTrend.ChartAreas[0].CursorX.IntervalType = DateTimeIntervalType.Seconds;
chartTrend.ChartAreas[0].CursorX.AutoScroll = false;
chartTrend.ChartAreas[0].AxisY.IsStartedFromZero = true;
chartTrend.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
chartTrend.ChartAreas[0].AxisY.ScaleView.Zoomable = false;
// disable zoom-reset button (only scrollbar's arrows are available)
chartTrend.ChartAreas[0].AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
chartTrend.ChartAreas[0].AxisX.ScrollBar.IsPositionedInside = true;
chartTrend.ChartAreas[0].AxisX.ScrollBar.Enabled = true;
chartTrend.ChartAreas[0].AxisX.ScaleView.SizeType = DateTimeIntervalType.Seconds;
chartTrend.ChartAreas[0].AxisX.ScaleView.Size = 528;
}
The problem is that, when I add data to the chart, It will not be shown until I click on the scroll. I even tried to move the scroll by software but it didn't work. what can I do?
By the way, it is all for the last line of initGraph() method. when I comment it out, data will be shown, but in the way that I'm not interested.
The problem was the position of scroll. I understood that when I added data to the chart, It was depicting chart from a time which was far away from the start of my time range and after clicking on the scroll, it took it to the proper time. So I decided to move the scroll after adding my first points and finally, the problem solved by this line:
chartTrend.ChartAreas[0].AxisX.ScaleView.Position = chartTrend.ChartAreas[0].AxisX.Minimum;
I have a TextBox. I want the user to be able to click inside it; when he does, a red marker line should appear between the two characters closest to the click position, and remain here. The user could do that multiple times.
I'm new to WPF, but I guess, as in Winforms, I will have to hack a messy OnRender method. So far, it's okay.
What I'd really like to know is: how to get the two closest characters to the click position?
I was about to do a pixel check but it seems pretty heavy.
You could try:
textBox.GetCharacterIndexFromPoint(point, true);
Well I found what I wanted to do, and it's way simpler than I thought (even though FINDING the actual way was a pain).
Just add this handler to your textbox's SelectionChanged event:
private void placeMarker(object sender, RoutedEventArgs e)
{
// txt_mark is your TextBox
int index = txt_mark.CaretIndex;
// I don't want the user to be able to place a marker at index = 0 or after the last character
if (txt_mark.Text.Length > first && first > 0)
{
Rect rect = txt_mark.GetRectFromCharacterIndex(first);
Line line = new Line();
// If by any chance, your textbox is in a scroll view,
// use the scroll view's margin instead
line.X1 = line.X2 = rect.Location.X + txt_mark.Margin.Left;
line.Y1 = rect.Location.Y + txt_mark.Margin.Top;
line.Y2 = rect.Bottom + txt_mark.Margin.Top;
line.HorizontalAlignment = HorizontalAlignment.Left;
line.VerticalAlignment = VerticalAlignment.Top;
line.StrokeThickness = 1;
line.Stroke = Brushes.Red;
// grid1 or any panel you have
grid1.Children.Add(line);
// set the grid position of the line to txt_mark's (or the scrollview's if there is one)
Grid.SetRow(line, Grid.GetRow(txt_mark));
Grid.SetColumn(line, Grid.GetColumn(txt_mark));
}
}
You might want to add some kerning or simply increase the font size for the markers not to ruin readability.
I have a chart, in it there is one chartarea with x-axis y-axis.
First of all, I have to set it to zoomable,
chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
By default, if I select a rectangular area using the mouse, the chart will zoom to the selected area.
But this is quite annoying because it is prone to false operation.
But if I do this:
chart1.ChartAreas[0].AxisY.ScaleView.Zoomable = false;
chart1.ChartAreas[0].AxisX.ScaleView.Zoomable = false;
The Axes won't zoom, even if I call
chart1.ChartAreas[0].AxisX.ScaleView.Zoom(a, b);
So, I want the chartarea to be zoomable, but I don't like the mouse selection ability.
I found a method,
void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
It seems that when I select a new area, this method will be called,
but it is not meant to be override.
What can I do?
Thank you!
try this:
var ca = chart1.ChartAreas["ChartArea1"];
ca.CursorX.IsUserEnabled = false;
ca.CursorX.IsUserSelectionEnabled = false;
(and same for CursorY, and replacing "ChartArea1" with the name of your chart area if it's different).
This will disable the mouse selection, so you won't risk accidental zooming anymore.
Just to explain what I'm doing, I draw two selectors on a chart, and the part that will not be selected should appear under that blue rectangle. The part that will be selected will appear in the white area, between the two selectors. The figure below shows only the left selector.
Now, what I'm trying to do is to draw a rectangle inside a chart that always remain inside the plotting area, even when the windows is resized.
To get the top, left and bottom bounds, to draw the rectangle as shown in the figure below, I do the following:
(...)
int top = (int)(Chart.Height * 0.07);
int bottom = (int)(Chart.Height - 1.83 * top);
int left = (int)(0.083 * Chart.Width);
Brush b = new SolidBrush(Color.FromArgb(128, Color.Blue));
e.Graphics.FillRectangle(b, left, top, marker1.X - left, bottom - top);
(...)
But that's far from perfect, and it isn't drawn in the right place when the window is resized. I want the blue rectangle to always be bound on the top, left and bottom by the plotting area grid. Is that possible?
You probably want to use StripLine to achieve this.
Look into the Stripline Class Documentation.
Also I recommend downloading the Charting Samples which are a great help to understand the various features.
StripLine stripLine = new StripLine();
stripLine.Interval = 0; // Set Strip lines interval to 0 for non periodic stuff
stripLine.StripWidth = 10; // the width of the highlighted area
stripline.IntervalOffset = 2; // the starting X coord of the highlighted area
// pick you color etc ... before adding the stripline to the axis
chart.ChartAreas["Default"].AxisX.StripLines.Add( stripLine );
This assumes you are wanting something that is not what Cursor already does (see CursorX), such as letting the user mark up areas of the plot which provides some persistence. Combining the Cursor events with the striplines above would be a good way to do that.
So to highlight the start and end of the cursor you could do this
// this would most likely be done through the designer
chartArea1.AxisX.ScaleView.Zoomable = false;
chartArea1.CursorX.IsUserEnabled = true;
chartArea1.CursorX.IsUserSelectionEnabled = true;
this.chart1.SelectionRangeChanged += new System.EventHandler<System.Windows.Forms.DataVisualization.Charting.CursorEventArgs>(this.chart1_SelectionRangeChanged);
...
private void chart1_SelectionRangeChanged(object sender, CursorEventArgs e)
{
chart1.ChartAreas[0].AxisX.StripLines.Clear();
StripLine stripLine1 = new StripLine();
stripLine1.Interval = 0;
stripLine1.StripWidth = chart1.ChartAreas[0].CursorX.SelectionStart - chart1.ChartAreas[0].AxisX.Minimum;
stripLine1.IntervalOffset = chart1.ChartAreas[0].AxisX.Minimum;
// pick you color etc ... before adding the stripline to the axis
stripLine1.BackColor = Color.Blue;
chart1.ChartAreas[0].AxisX.StripLines.Add(stripLine1);
StripLine stripLine2 = new StripLine();
stripLine2.Interval = 0;
stripLine2.StripWidth = chart1.ChartAreas[0].AxisX.Maximum - chart1.ChartAreas[0].CursorX.SelectionEnd;
stripLine2.IntervalOffset = chart1.ChartAreas[0].CursorX.SelectionEnd;
// pick you color etc ... before adding the stripline to the axis
stripLine2.BackColor = Color.Blue;
chart1.ChartAreas[0].AxisX.StripLines.Add(stripLine2);
}
Somehow I suspect you may not have discovered the cursor yet, and doing so will make all this irrelevant. But anyway, the above code will do what you described.
I'm busy with a small application in which I want to display information at the location of the cursor when it hoovers over a Canvas. The Canvas in question is a custom one (inherited from Canvas) which provides functionality to add DrawingVisuals (as shown in basically every tutorial on displaying large amounts of geometric shapes on a canvas).
I would like to display a vertical line and horizontal line as well as the local coordinates (p in the code below) which are directly derived from the canvas coordinates (v). At the moment I'm rendering these objects at position (0,0) and use offset during the OnMouseMove event to update their location.
The horizontal and vertical lines are rendered in the DrawingVisual _cursor and the location in local y,z-coordinates in _info.
private void oCanvas_MouseMove(object sender, MouseEventArgs e)
{
#region 1. Get location data
System.Windows.Vector v = (System.Windows.Vector)e.GetPosition(oCanvas);
// point in YZ coordinates
BSMath.DoubleXY p = new BSMath.DoubleXY();
p.X = (oCanvas.OriginY - v.Y) / oCanvas.ZoomFactor;
p.Y = (oCanvas.OriginX - v.X) / oCanvas.ZoomFactor;
#endregion
#region 2. Update cursor and info
if (oSettings.ShowInformation)
{
_info.Info = p.X.ToString("0.0") + " | " + p.Y.ToString("0.0");
_info.Render(0, 0);
_info.Visual.Offset = v;
}
// move cursor
_cursor.Visual.Offset = v;
}
Using the mousemove event seems to be creating a lot of overhead and I can see that there are issues tracking the mouse movements when I move the mouse quickly.
Can anyone recommend a better way of creating the same effect?
example http://www.iccg.be/test/images/canvas.jpg
Edit:
I investigated it a bit further and the problem seems to occur when the resolution of the canvas is bigger. If it is a 600x400 canvas then there is no delay, but when it is around 1000x800 I get the problem with delays when hoovering. The performance also improves if I use user drawn crosshairs instead of the lines that have the full width/ height of the canvas.
I recently have built something similar and haven't had any performance issues.
Did it the very simple way by adding the stuff directly on the canvas.
The drawn items are in a second canvas behind the mouse position canvas. Both reside in a Grid.
This is for sure not the most sophisticated way to solve this, but it works quite well for me.
Here's the code:
private Point _previous;
private Point _current;
private Line _xLine;
private Line _yLine;
private TextBlock _displayTextBlock;
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
_current = e.GetPosition(myCanvas);
if (_previous != _current)
{
if (_xLine == null)
{
_xLine = new Line() {X1 = 0, X2 = myCanvas.ActualWidth, Stroke = new SolidColorBrush(Colors.Black)};
_yLine = new Line() {Y1 = 0, Y2 = myCanvas.ActualHeight, Stroke = new SolidColorBrush(Colors.Black)};
_displayTextBlock = new TextBlock();
myCanvas.Children.Add(_xLine);
myCanvas.Children.Add(_yLine);
myCanvas.Children.Add(_displayTextBlock);
}
_displayTextBlock.SetValue(Canvas.TopProperty, _current.Y);
_displayTextBlock.SetValue(Canvas.LeftProperty, _current.X);
_displayTextBlock.Text = _current.X.ToString() + " | " + _current.Y.ToString();
_xLine.Y1 = _current.Y;
_xLine.Y2 = _current.Y;
_yLine.X1 = _current.X;
_yLine.X2 = _current.X;
_previous = _current;
}
}