带滑块的趋势图(slider control in chart)
如果没记错的话,好像是第一次在用趋势图显示数据的时候,就遇到过这个问题,当时是在一个趋势图中显示一个月中每天的数据趋势变化,结果就是根本在一张图中显示不下,X轴的文字标注都重叠到一起,当时用的是PB,里面的图形显示功能非常强大,后来好像是将文字直立显示的,避免了标注的重叠。
最近同时好像在搞趋势图,又想起这个问题,就试着在里面加个slider实现拖动以便可以显示“被多出”的部分。老规矩,先看图。
嘿嘿,其实也没用到什么,纯GDI+,用到的也不外乎就是双缓存和坐标变换。主要处理的事件就是MouseDown和MouseMove,重绘Paint。在MouseDown中变化X轴的值和Value值(这里也有变换坐标),在MouseMove中变换坐标,Paint中绘制轴、趋势图、滑块。
下面看看完整的代码:
代码
/// <summary>
/// X轴和Value值重加载
/// </summary>
private void lbchart_MouseDown(object sender, MouseEventArgs e)
{
//是否点中滑块
IsClick = IsClickSlider(new Point(e.X, e.Y));
//左键有效
if (e.Button == MouseButtons.Left)
{
double disvalue = 0; //位移
if (curPoint.X != e.X || curPoint.Y != e.Y)
{
curPoint.X = e.X;
curPoint.Y = e.Y;
Capture = true;
//是否需要移动
if (IsClick)
{
IsNeedMove = true;
}
//位移差
disvalue =curPoint.X - oldPoint.X;
}
else
{
IsNeedMove = false;
//位移差
disvalue = dx;
}
//重新复制PartValue中的值
//****x轴移动的位移
int xd = (int)Math.Round(disvalue / xdvalue);
curArrPos = curArrPos + xd;
if (curArrPos < 0)
{
curArrPos = 0;
}
if (curArrPos > 6)
{
curArrPos = 6;
}
for (int i = curArrPos; i < curArrPos + 7; i++)
{
xPartValue[i - curArrPos] = xValue[i];
partValue[i - curArrPos] = value[i];
}
}
if (e.Button == MouseButtons.Right)
{
Capture = false;
}
dx = 0;
this.lbchart.Invalidate();
}
/// <summary>
/// 鼠标移动
/// </summary>
private void lbchart_MouseMove(object sender, MouseEventArgs e)
{
Graphics g2 = CreateGraphics();
if (IsNeedMove)
{
dx = dx + (e.X - oldPoint.X);
curPoint = new Point(e.X, e.Y);
}
this.lbchart.Invalidate();
}
/// X轴和Value值重加载
/// </summary>
private void lbchart_MouseDown(object sender, MouseEventArgs e)
{
//是否点中滑块
IsClick = IsClickSlider(new Point(e.X, e.Y));
//左键有效
if (e.Button == MouseButtons.Left)
{
double disvalue = 0; //位移
if (curPoint.X != e.X || curPoint.Y != e.Y)
{
curPoint.X = e.X;
curPoint.Y = e.Y;
Capture = true;
//是否需要移动
if (IsClick)
{
IsNeedMove = true;
}
//位移差
disvalue =curPoint.X - oldPoint.X;
}
else
{
IsNeedMove = false;
//位移差
disvalue = dx;
}
//重新复制PartValue中的值
//****x轴移动的位移
int xd = (int)Math.Round(disvalue / xdvalue);
curArrPos = curArrPos + xd;
if (curArrPos < 0)
{
curArrPos = 0;
}
if (curArrPos > 6)
{
curArrPos = 6;
}
for (int i = curArrPos; i < curArrPos + 7; i++)
{
xPartValue[i - curArrPos] = xValue[i];
partValue[i - curArrPos] = value[i];
}
}
if (e.Button == MouseButtons.Right)
{
Capture = false;
}
dx = 0;
this.lbchart.Invalidate();
}
/// <summary>
/// 鼠标移动
/// </summary>
private void lbchart_MouseMove(object sender, MouseEventArgs e)
{
Graphics g2 = CreateGraphics();
if (IsNeedMove)
{
dx = dx + (e.X - oldPoint.X);
curPoint = new Point(e.X, e.Y);
}
this.lbchart.Invalidate();
}
上面是两个主要的鼠标事件,下面看看绘制代码:
代码
#region 重绘
private void lbchart_Paint(object sender, PaintEventArgs e)
{
//初始化值
g0 = e.Graphics;
bitmap = new Bitmap(lbchart.Width, lbchart.Height);
g1 = Graphics.FromImage(bitmap);
g1.Clear(this.lbchart.BackColor);
g1.SmoothingMode = SmoothingMode.AntiAlias;
#region 绘制
//轴
DrawAxes();
//趋势图
DrawChart();
//滑块
DrawSlider();
#endregion
//绘制到lb上
g0.DrawImage(bitmap, 0, 0);
}
/// <summary>
/// 绘制轴
/// </summary>
private void DrawAxes()
{
Brush axesbrush = new SolidBrush(Color.Black);
Font axesfont = new Font("宋体", 15, FontStyle.Bold);
Font valuefont = new Font("宋体", 10, FontStyle.Regular);
//箭头
AdjustableArrowCap cap = new AdjustableArrowCap(3, 3);
cap.WidthScale = 3;
cap.BaseCap = LineCap.Square;
cap.Height = 3;
axesPen.CustomStartCap = cap;
//****Y轴
g1.DrawLine(axesPen, lbPosX + 10, lbPosY + 10, lbPosX + 10, lbHeight - 30);
g1.DrawString("Y", axesfont, axesbrush, lbPosX + 15, lbPosY + 10);
//*****Y轴差值
ydvalue = ((lbHeight - 15) - (lbPosY + 10)) / (yValue.Length + 1);
for (int i = 0; i < yValue.Length; i++)
{
float y = (float)(lbHeight - 15 - (i + 1) * ydvalue);
g1.DrawString(yValue[i].ToString(), valuefont, axesbrush, lbPosX + 12, y);
g1.DrawLine(valuePen, lbPosX + 10, y, lbPosX + 15, y);
}
//****X轴
g1.DrawLine(axesPen, lbWidth - 5, lbHeight - 30, lbPosX + 10, lbHeight - 30);
g1.DrawString("X", axesfont, axesbrush, lbWidth - 15, lbHeight - 55);
//******X轴差值
xdvalue = (lbWidth - 5 - (lbPosX + 10)) / (partValue.Length - 1);
for (int i = 0; i < xPartValue.Length; i++)
{
float x = (float)(lbPosX + 10 + (i) * xdvalue);
if (Convert.ToDouble(xPartValue[i].ToString()) >= 10)
{
g1.DrawString(xPartValue[i].ToString(), valuefont, axesbrush, x - 12, lbHeight - 28);
}
else
{
g1.DrawString(xPartValue[i].ToString(), valuefont, axesbrush, x - 5, lbHeight - 28);
}
g1.DrawLine(valuePen, x, lbHeight - 30, x, lbHeight - 35);
}
}
/// <summary>
/// 绘制趋势图
/// </summary>
private void DrawChart()
{
//****绘制曲线图
double dvalue = ydvalue / 5.0;
for (int i = 0; i < partValue.Length - 1; i++)
{
double startX = lbPosX + 10 + (i) * xdvalue;
double endX = startX + xdvalue;
double startY = Convert.ToDouble(partValue[i].ToString()) * dvalue;
double endY = Convert.ToDouble(partValue[i + 1].ToString()) * dvalue;
g1.DrawEllipse(new Pen(Color.Red), (float)(startX - 3), (float)(startY - 3), 6, 6);
g1.DrawLine(valuePen, (float)endX, (float)endY, (float)startX, (float)startY);
}
}
/// <summary>
/// 绘制滑块
/// </summary>
private void DrawSlider()
{
//绘制滑块
if (curPoint.X != 0 && curPoint.Y != 0)
{
oldPoint = curPoint;
g1.DrawLine(sliderPen, lbPosX + curPoint.X, lbPosY, lbPosX + curPoint.X, lbPosY + lbHeight);
}
else
{
g1.DrawLine(sliderPen, lbWidth / 2, lbPosY, lbWidth / 2, lbPosY + lbHeight);
}
}
#endregion
private void lbchart_Paint(object sender, PaintEventArgs e)
{
//初始化值
g0 = e.Graphics;
bitmap = new Bitmap(lbchart.Width, lbchart.Height);
g1 = Graphics.FromImage(bitmap);
g1.Clear(this.lbchart.BackColor);
g1.SmoothingMode = SmoothingMode.AntiAlias;
#region 绘制
//轴
DrawAxes();
//趋势图
DrawChart();
//滑块
DrawSlider();
#endregion
//绘制到lb上
g0.DrawImage(bitmap, 0, 0);
}
/// <summary>
/// 绘制轴
/// </summary>
private void DrawAxes()
{
Brush axesbrush = new SolidBrush(Color.Black);
Font axesfont = new Font("宋体", 15, FontStyle.Bold);
Font valuefont = new Font("宋体", 10, FontStyle.Regular);
//箭头
AdjustableArrowCap cap = new AdjustableArrowCap(3, 3);
cap.WidthScale = 3;
cap.BaseCap = LineCap.Square;
cap.Height = 3;
axesPen.CustomStartCap = cap;
//****Y轴
g1.DrawLine(axesPen, lbPosX + 10, lbPosY + 10, lbPosX + 10, lbHeight - 30);
g1.DrawString("Y", axesfont, axesbrush, lbPosX + 15, lbPosY + 10);
//*****Y轴差值
ydvalue = ((lbHeight - 15) - (lbPosY + 10)) / (yValue.Length + 1);
for (int i = 0; i < yValue.Length; i++)
{
float y = (float)(lbHeight - 15 - (i + 1) * ydvalue);
g1.DrawString(yValue[i].ToString(), valuefont, axesbrush, lbPosX + 12, y);
g1.DrawLine(valuePen, lbPosX + 10, y, lbPosX + 15, y);
}
//****X轴
g1.DrawLine(axesPen, lbWidth - 5, lbHeight - 30, lbPosX + 10, lbHeight - 30);
g1.DrawString("X", axesfont, axesbrush, lbWidth - 15, lbHeight - 55);
//******X轴差值
xdvalue = (lbWidth - 5 - (lbPosX + 10)) / (partValue.Length - 1);
for (int i = 0; i < xPartValue.Length; i++)
{
float x = (float)(lbPosX + 10 + (i) * xdvalue);
if (Convert.ToDouble(xPartValue[i].ToString()) >= 10)
{
g1.DrawString(xPartValue[i].ToString(), valuefont, axesbrush, x - 12, lbHeight - 28);
}
else
{
g1.DrawString(xPartValue[i].ToString(), valuefont, axesbrush, x - 5, lbHeight - 28);
}
g1.DrawLine(valuePen, x, lbHeight - 30, x, lbHeight - 35);
}
}
/// <summary>
/// 绘制趋势图
/// </summary>
private void DrawChart()
{
//****绘制曲线图
double dvalue = ydvalue / 5.0;
for (int i = 0; i < partValue.Length - 1; i++)
{
double startX = lbPosX + 10 + (i) * xdvalue;
double endX = startX + xdvalue;
double startY = Convert.ToDouble(partValue[i].ToString()) * dvalue;
double endY = Convert.ToDouble(partValue[i + 1].ToString()) * dvalue;
g1.DrawEllipse(new Pen(Color.Red), (float)(startX - 3), (float)(startY - 3), 6, 6);
g1.DrawLine(valuePen, (float)endX, (float)endY, (float)startX, (float)startY);
}
}
/// <summary>
/// 绘制滑块
/// </summary>
private void DrawSlider()
{
//绘制滑块
if (curPoint.X != 0 && curPoint.Y != 0)
{
oldPoint = curPoint;
g1.DrawLine(sliderPen, lbPosX + curPoint.X, lbPosY, lbPosX + curPoint.X, lbPosY + lbHeight);
}
else
{
g1.DrawLine(sliderPen, lbWidth / 2, lbPosY, lbWidth / 2, lbPosY + lbHeight);
}
}
#endregion