protected override void OnPaint(PaintEventArgs e) { int height = ClientSize.Height; int pixelsPerHour = (PixelsPerHour * (_Scrollable ? ZoomRatio : 128)) / 128; long maxValue = MaximumThreshold; long minTicks = 0, maxTicks = 0; int contentWidth; const long hourInTicks = Squared.Util.Time.SecondInTicks * 60 * 60; if (Items.Count > 0) { maxValue = Math.Max(MaximumThreshold, (from s in Items select _ItemValueGetter(s)).Max()); minTicks = (from s in Items select s.Timestamp.Ticks).Min(); maxTicks = (from s in Items select s.Timestamp.Ticks).Max(); bool rescaled = false; rescale: _ContentWidth = contentWidth = (int)( (maxTicks - minTicks) * pixelsPerHour / hourInTicks ) + MarginWidth; if (!_Scrollable && !rescaled) { var ratio = (ClientSize.Width / (double)(contentWidth + 1)) * 128.0; _ZoomRatio = (int)Math.Floor(ratio); if (_ZoomRatio < 1) { _ZoomRatio = 1; } pixelsPerHour = (PixelsPerHour * _ZoomRatio) / 128; if (pixelsPerHour < 1) { pixelsPerHour = 1; } rescaled = true; goto rescale; } } else { _ContentWidth = contentWidth = MarginWidth; } if (_Scrollable) { height -= ScrollBar.Height; if (_ScrollOffset > contentWidth - ClientSize.Width) { _ScrollOffset = contentWidth - ClientSize.Width; } if (_ScrollOffset < 0) { _ScrollOffset = 0; } int scrollMax = contentWidth - ClientSize.Width + ScrollBar.LargeChange - 1; bool enabled = (scrollMax > 0); if (scrollMax < 0) { scrollMax = 0; } // WinForms' scrollbar control is terrible if (ScrollBar.Maximum != scrollMax) { ScrollBar.Maximum = scrollMax; } if (ScrollBar.Value != _ScrollOffset) { ScrollBar.Value = _ScrollOffset; } if (ScrollBar.Enabled != enabled) { ScrollBar.Enabled = enabled; } if (!ScrollBar.Visible) { ScrollBar.Visible = true; } if (!ZoomInButton.Visible) { ZoomInButton.Visible = true; } if (!ZoomOutButton.Visible) { ZoomOutButton.Visible = true; } } else { if (ScrollBar.Visible) { ScrollBar.Visible = false; } if (ZoomInButton.Visible) { ZoomInButton.Visible = false; } if (ZoomOutButton.Visible) { ZoomOutButton.Visible = false; } _ScrollOffset = 0; } using (var outlinePen = new Pen(Color.Black)) using (var gridPen = new Pen(Color.FromArgb(96, 0, 0, 0))) using (var scratch = Scratch.Get(e.Graphics, ClientRectangle)) { var g = scratch.Graphics; g.Clear(BackColor); g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; float y = 0; using (var textBrush = new SolidBrush(ForeColor)) { g.DrawLine(gridPen, 0, height - VerticalMargin, ClientSize.Width, height - VerticalMargin); g.DrawLine(gridPen, 0, VerticalMargin, Width, VerticalMargin); var lineHeight = g.MeasureString("AaBbYyZz", Font).Height; var numLines = (int)Math.Min( Math.Max(2, Math.Floor((height / lineHeight) * GridLineRatio)), MaxGridLines ); for (int i = 0; i <= numLines; i++) { var value = (maxValue * i / numLines); var text = _ItemValueFormatter(value); y = (float)(height - VerticalMargin - ((value / (double)maxValue) * (height - VerticalMargin * 2))); g.DrawLine(gridPen, 0, y, ClientSize.Width, y); g.DrawString(text, Font, textBrush, new PointF(4, y)); } } var itemXCoordinates = (from item in Items select (int)((item.Timestamp.Ticks - minTicks) * pixelsPerHour / hourInTicks ) - _ScrollOffset).ToArray(); var itemYCoordinates = (from item in Items select (float)(height - VerticalMargin - ( (_ItemValueGetter(item) / (double)maxValue) * (height - VerticalMargin * 2) ))); var valueSeriesYCoordinates = ( from series in DataSeries.Values select( from value in series select (float)(height - VerticalMargin - ( (value / (double)maxValue) * (height - VerticalMargin * 2) )) ) ); var firstIndex = Math.Max(0, IndexFromPoint(new Point(0, 0), -1)); using (var brush = new SolidBrush(Color.FromArgb(127, ForeColor))) DrawValueSeries( g, itemXCoordinates.Skip(firstIndex), itemYCoordinates.Skip(firstIndex), ClientSize.Width, height, brush, outlinePen ); int seriesIndex = 0; foreach (var series in valueSeriesYCoordinates) { int hue = (HSV.HueMax * seriesIndex) / DataSeries.Count; var seriesColor = HSV.ColorFromHSV( (UInt16)hue, HSV.SaturationMax, HSV.ValueMax ); using (var brush = new SolidBrush(Color.FromArgb(15, seriesColor))) using (var pen = new Pen(Color.FromArgb(160, seriesColor))) DrawValueSeries( g, itemXCoordinates.Skip(firstIndex), series.Skip(firstIndex), ClientSize.Width, height, brush, pen ); seriesIndex += 1; } firstIndex = Selection.First; var takeCount = Selection.Second - Selection.First + 1; using (var brush = new SolidBrush(SystemColors.Highlight)) using (var pen = new Pen(SystemColors.HighlightText) { Width = 2.0f }) DrawValueSeries( g, itemXCoordinates.Skip(firstIndex).Take(takeCount), itemYCoordinates.Skip(firstIndex).Take(takeCount), ClientSize.Width, height, brush, pen ); var cursorPos = PointToClient(Cursor.Position); DateTime mouseTime; if (ClientRectangle.Contains(cursorPos) && TimeFromPoint(_MouseMoveLocation, out mouseTime)) { using (var pen = new Pen(Color.FromArgb(127, SystemColors.HighlightText))) { pen.Width = 2.0f; g.DrawLine(pen, _MouseMoveLocation.X, 0, _MouseMoveLocation.X, height); } var mouseIndex = IndexFromTime(mouseTime, -1); if (!_MouseDownLocation.HasValue) { using (var pen = new Pen(SystemColors.HighlightText)) { pen.Width = 2.0f; var item = Items[mouseIndex]; var x = (int)((item.Timestamp.Ticks - minTicks) * pixelsPerHour / hourInTicks) - _ScrollOffset; g.DrawLine( pen, x, 0, x, height ); } } } } }