[EDIT]
After a lot of trial and error, I realized a version of my tooltip code could scroll while the rest of the codes are unchanged. The only difference between the two tooltip usage is that the pos.x value is shifted, instead of being exactly where the mouse is.
Which means instead of
/*X-axis Tooltip*/
tooltip.Show(Math.Truncate(xValue * 1000) / 1000 + unit_Converter(), this.chart1, pos.X, pos.Y - 15);
I did this
/*X-axis Tooltip*/
tooltip.Show(Math.Truncate(xValue * 1000) / 1000 + unit_Converter(), this.chart1, pos.X - 70, pos.Y - 15);
That was all the difference. Now I can click and drag the X-axis scroll bar around. I suppose the reason why I always could scroll my Y-axis is it was shifted by 15 to begin with.
So if you set your tooltip position exactly where your mouse position is, then apparently you will be clicking on the tooltip itself, instead of the scrollbar when you attempt to scroll.
Answer provided by Josh W is equally valid, because using just "this" instead of "this.chart" automatically shifts the tooltip a little bit for some reason. thanks for the help!
[Original Question]
I have a chart that has Y-axis and X-axis scroll bar. For a while both my scrollbars worked. As my code grow, the X-axis scrollbar now won't move, while the Y-axis scrollbar moves just fine. I am puzzled. Any help would be appreciated.
I have referenced other answers such as
c# chart control, vertical scrolling problems with zoom ["Stuck Scroll bar"]
But X-axis scrollbar still stuck....
[EDIT]:
I realized that my tooltip on mouse move code is causing this. If I disable my call to my mouse move code, the X-axis scrollbar would be functional again. But how do I make it so that both can function? I don't really know how the crossair tooltip would disable scrolling on the X-axis only, but not the Y-axis...
void chart1_MouseMove(object sender, MouseEventArgs e)
{
var pos = e.Location;
_point.X = e.Location.X;
_point.Y = e.Location.Y;
try
{
if ((chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) >= 0) && (chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X) <= max))
{
//Crossair
chart1.ChartAreas[0].CursorX.SetCursorPixelPosition(_point, true);
//Tooltips
double xValue = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
/*X-axis Tooltip*/
tooltip.Show(Math.Truncate(xValue * 1000) / 1000 + unit_Converter(), this.chart1, pos.X, pos.Y - 15);
}
}
catch (Exception exception)
{
}
}
My code to set series settings:
var series = chart1.ChartAreas[chart1.Series[iname].ChartArea];
//Line thickness
chart1.Series[iname].BorderWidth = 2;
series.AxisX.Minimum = 0;
series.AxisX.Maximum = max;
series.AxisY.Minimum = 0;
series.AxisY.Maximum = checkedListBox1.CheckedItems.Count * 3 - 2;
series.AxisX.MajorGrid.Interval = time_of_cycle;
series.AxisX.MajorGrid.LineDashStyle = ChartDashStyle.DashDotDot;
series.AxisY.MajorGrid.Interval = 2;
series.CursorX.Interval = 0;
series.CursorY.Interval = 0;
series.AxisX.ScaleView.SmallScrollSize = time_of_cycle /100 ;
series.AxisY.ScaleView.SmallScrollSize = 1;
//Disables Y axis lable
series.AxisY.LabelStyle.Enabled = false;
series.AxisX.LabelStyle.ForeColor = Color.White;
series.AxisY.LabelStyle.ForeColor = Color.White;
series.AxisX.LabelStyle.Format = label_Style_Converter();
series.AxisX.LabelStyle.Interval = time_of_cycle * 2;
series.AxisX.MajorGrid.LineColor = Color.DimGray;
series.AxisY.MajorGrid.LineColor = Color.DimGray;
series.AxisX.ScrollBar.BackColor = Color.LightGray;
series.AxisY.ScrollBar.BackColor = Color.LightGray;
series.AxisX.ScrollBar.ButtonColor = Color.LightGray;
series.AxisY.ScrollBar.ButtonColor = Color.LightGray;
series.AxisX.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
series.AxisY.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
series.AxisX.ScrollBar.Enabled = true;
series.AxisY.ScrollBar.Enabled = true;
series.AxisX.ScrollBar.IsPositionedInside = false;
series.AxisY.ScrollBar.IsPositionedInside = false;
series.AxisX.IsMarginVisible = true;
series.AxisY.IsMarginVisible = false;
series.AxisX.Name = "µs";
series.AxisX.ScaleView.Size = max - time_of_cycle / 100;
series.AxisY.ScaleView.Size = (checkedListBox1.CheckedItems.Count * 3 - 2) + 1 ;
series.BackColor = Color.Black;
//crosshair
var cursor_Y = chart1.ChartAreas["ChartArea1"].CursorY;
var cursor_X = chart1.ChartAreas["ChartArea1"].CursorX;
cursor_Y.LineWidth = 1;
cursor_Y.LineDashStyle = ChartDashStyle.Solid;
cursor_Y.LineColor = Color.DarkRed;
cursor_Y.SelectionColor = Color.LightGray;
cursor_X.LineWidth = 1;
cursor_X.LineDashStyle = ChartDashStyle.Solid;
cursor_X.LineColor = Color.DarkRed;
chart1.MouseMove += new MouseEventHandler(chart1_MouseMove);
At first I thought maybe your call to .Show() was blocking the GUI thread, but some quick and dirty code didn't seem to have that issue. One odd thing I did notice with the tooltip though is that when you have something like your Chart subscribed to the MouseMove event, and your tool-tip is given a 'this.chart1' reference instead of just this, then if the tooltip is under your mouse, it continues to fire the event.
That is, even if the tooltip moves off the chart for example.
Here was some test code I just played with using a panel instead of a chart.
private int Counter = 0;
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
Console.WriteLine(string.Format("X{0}, Y{1}\t Count = {2}", e.X, e.Y, Counter));
Counter++;
toolTip1.Show(
string.Format("X{0}, Y{1}\t Count = {2}", e.X, e.Y, Counter),
this.panel1,
e.X - 75,
e.Y -5);
}
First you should also get rid of that try/catch statement as you aren't doing anything with the error... either handle errors, or let them bubble up to crash something (so that you can 'fix' it). What you have there now might throw an error, but you'd never know, because you are catching it and ignoring it.
Second, the MouseMove event happens a LOT... perhaps use the MouseHover for showing the tooltip?
Other than that... it does not appear to be where your code is breaking, though it could be inside the unit_Converter() call or even on the math function... your try/catch may be hiding an exception on that line.
Related
Currently, I have been doing a project on graphs on windows form. Now I have something to ask.
Is there by any chance a way to move the Stripeline label out of my chart and not have it in the chart to something like this What I expect it to be and also make my stripeline tinner because it is too thick
till date what i do
What I expect it to be
This is my stripline code in case you need it
StripLine stripLine1 = new StripLine();
stripLine1.StripWidth = 0.01;
stripLine1.BorderColor = System.Drawing.Color.Blue;
stripLine1.BorderWidth = 0;
stripLine1.BorderDashStyle = ChartDashStyle.Solid;
stripLine1.IntervalOffset = Convert.ToDouble(textBox7.Text);
stripLine1.BackColor = System.Drawing.Color.Blue;
stripLine1.Text = "x̅";
chart1.ChartAreas[0].AxisY.StripLines.Add(stripLine1);
No, StripLines are drawn inside their ChartArea only.
There are at least two options:
You can display their Text outside of it by coding a PaintXXX event, maybe like this:
private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
ChartArea ca = chart1.ChartAreas[0];
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
Graphics g = e.ChartGraphics.Graphics;
foreach (StripLine sl in ay.StripLines)
{
double v = (sl.Interval != double.NaN ? sl.Interval : 0) + sl.IntervalOffset;
TextRenderer.DrawText(g, sl.Text, sl.Font, new Point(
(int)ax.ValueToPixelPosition(ax.Maximum),
(int)ay.ValueToPixelPosition(v)),Color.Black);
}
}
Fine-tuning the position, maybe by honoring the TextAlignment properties is up to you..
Note that this will now display the text twice; a simple solution it to make the original label transparent:
YourStripLine.ForeColor = Color.Transparent;
Also note that drawn graphics will not get exported if you serialize the chart.
An alternative would be to set the secondary y-axis and add the labels as CustomLabels. On second thought, I think this is preferrable, at least if you don't need the secondary y-axis.
Here I do it for one stripline:
Axis ay2 = chart1.ChartAreas[0].AxisY2;
ay2.Enabled = AxisEnabled.True;
ay2.LineColor = Color.Transparent;
CustomLabel cl = new CustomLabel();
cl.Text = stripLine.Text;
double v = (stripLine.Interval != double.NaN ? stripLine.Interval : 0)
+ stripLine.IntervalOffset;
cl.FromPosition = v - 0.001;
cl.ToPosition = v + 0.001;
ay2.CustomLabels.Add(cl);
You here also will want to make the original text transparent.
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 have a line chart. I read a value and its recording time from a database and add those to line chart every second. So my points increases every second.
The problem is that. I added a line and rectangle annonation to my line chart. Line annonation stay at same width as it had at the beginning but rectangle annonation's width changes acording to the points' count i have added.
Like this;
I just want that rectangle annonation have a fixed width How can i do that?
Code;
VerticalLineAnnotation VA;
RectangleAnnotation RA;
double xFactor = 0.03;
double yFactor = 0.02;
VA = new VerticalLineAnnotation();
VA.AxisX = chartMonitor.ChartAreas[0].AxisX;
VA.AllowMoving = true;
VA.IsInfinitive = true;
VA.ClipToChartArea = chartMonitor.ChartAreas[0].Name;
VA.Name = "myLine";
VA.LineColor = Color.Red;
VA.LineWidth = 2;
VA.X = 1;
RA = new RectangleAnnotation();
RA.AxisX = chartMonitor.ChartAreas[0].AxisX;
RA.IsSizeAlwaysRelative = false;
RA.Width = 10 * xFactor;
RA.Height = 30 * yFactor;
VA.Name = "myRect";
RA.LineColor = Color.Red;
RA.BackColor = Color.Red;
RA.AxisY = chartMonitor.ChartAreas[0].AxisY;
RA.Y = -RA.Height;
RA.X = VA.X - RA.Width / 2;
RA.Text = "M1";
RA.ForeColor = Color.White;
RA.Font = new System.Drawing.Font("Arial", 8f);
chartMonitor.Annotations.Add(VA);
chartMonitor.Annotations.Add(RA);
chartMonitor.AnnotationPositionChanged += new EventHandler(chartMonitor_AnnotationPositionChanged);
chartMonitor.AnnotationPositionChanging += new EventHandler<AnnotationPositionChangingEventArgs>(chartMonitor_AnnotationPositionChanging);
void chartMonitor_AnnotationPositionChanging(object sender, AnnotationPositionChangingEventArgs e)
{
if (sender == VA) RA.X = VA.X - RA.Width / 2;
}
void chartMonitor_AnnotationPositionChanged(object sender, EventArgs e)
{
VA.X = (int)(VA.X + 0.5);
RA.X = VA.X - RA.Width / 2;
}
I think I remember that piece of code..
The Annotation is bound to the X-Axis, which means most of its Properties are measured in X-Axis values. (Not the LineWidth, though, which is in Pixels or the FontSize, which works as usual..)
I had included a factor to let you scale things as needed.
Assuming a value f1 = 0.03 works well for N1 data points you will need to re-calculate for N2 data points like this:
xFactor = f1 * N1 / N2;
So you should introduce a const f1 and re-calculate the factor when you add or remove plot points..
Here is a function that will do that:
void scaleAnnotation(Annotation A)
{
ChartArea CA = chart1.ChartAreas[0];// pick your chartarea..
A.AxisX = CA.AxisX; // .. and axis!
int N = S1.Points.Count; // S1 being your Series !
// to keep the label width constant the chart's width must be considered
// 60 is my 'magic' number; you must adapt for your chart's x-axis scale!
double xFactor = 60 * N / chart1.Width;
A.Width = 1 * xFactor ;
A.X = VA.X - A.Width / 2;
}
I found that even when resizing the chart, adding 10.000 points and removing them one by one the label's width is pretty much constant.
Note: You will have to adapt the xFactor to your Chart's x-Values scale; do a few tests..!
I find that calling this every time I add a point keeps the label at a pretty constant size even when the chart itself has hundereds of points.
i am trying to do scroll and mousewheel, and dragging in a panel with textboxes in it. I have to let some of the textboxes with fixed position, but doing this, i have some problem with pixels, while Wheeling or dragging or scrolling. I do not know if there is some sort of way of doing this without this problem. If anyone know an answer and he/she are willing to help, please, feel free.
I will put the code i use for this:
This code is Inside a function that is called from Dynamics NAV and will create the operations:
#region PROPRIETES DU PANEL
pnOperation.Name = idOperationL.ToString();
pnOperation.BorderStyle = BorderStyle.FixedSingle;
pnOperation.BackColor = Color.LightGray;
pnOperation.Size = new Size(220, 40);
pnOperation.Top = posOperationL * 40;
pnOperation.AllowDrop = true;
if (nRolesG > 6)
{
//I fixed the operation here so they will stay at position 0
pnOperation.Top = posOperationL * 40;
pnOperation.Left = 0;
MainPanel.MouseWheel += (senderL, eL) =>
{
pnOperation.Left = 0;
pnOperation.Invalidate();
};
MainPanel.Scroll += (senderL, eL) =>
{
pnOperation.Left = 0;
pnOperation.Invalidate();
};
}
#endregion
Screenshot
Screenshot
Thanks in advance
In my current project I have a problem when I add my usercontrol into the panel of splitcontainer. I managed to add it to the middle of the panel with the following code:
ucFactuur ucFactuur = new ucFactuur();
ucFactuur.Location = new Point(
splitContainer1.Panel2.ClientSize.Width / 2 - ucFactuur.Size.Width / 2,
splitContainer1.Panel2.ClientSize.Height / 2 - ucFactuur.Size.Height / 2);
ucFactuur.Anchor = AnchorStyles.None;
splitContainer1.Panel2.Controls.Add(ucFactuur);
But now my scrollbar is gone, it is there when I remove the AnchorStyles.None but then when I resize the window it doesn't stay in the middle (It's in a fixed position).
I'm uncertain how to resolve this problem, nor can I find any other way to dynamically center my usercontrol.
Thanks,
Thomas
Anchoring.None won't work in this situation since it only works when there are no scrollbars. But once you have scrollbars, you don't want the control centered anymore, you need it positioned against the scroll value.
In other words, I think you have to handle the resizing yourself:
private void DoResize(object sender, EventArgs e) {
splitContainer1.Panel2.AutoScrollMinSize = ucFactuur.Size;
if (ucFactuur.Width < splitContainer1.Panel2.ClientSize.Width) {
ucFactuur.Left = splitContainer1.Panel2.ClientSize.Width / 2 -
ucFactuur.Width / 2;
} else {
ucFactuur.Left = splitContainer1.Panel2.AutoScrollPosition.X;
}
if (ucFactuur.Height < splitContainer1.Panel2.ClientSize.Height) {
ucFactuur.Top = splitContainer1.Panel2.ClientSize.Height / 2 -
ucFactuur.Height / 2;
} else {
ucFactuur.Top = splitContainer1.Panel2.AutoScrollPosition.Y;
}
}
Then your setup would change to this:
ucFactuur ucFactuur = new ucFactuur();
ucFactuur.AutoSize = true;
ucFactuur.Resize += DoResize;
splitContainer1.Panel2.Resize += DoResize;
splitContainer1.Panel2.AutoScroll = false;
splitContainer1.Panel2.Controls.Add(ucFactuur);