MSCharts custom panning

2019-06-14 02:12发布

I'm trying to make my own custom panning feature in MSCharts (Microsoft Charting Controls). I realize there is an extension for zooming and panning, however it doesn't have a lot of the capabilities that I want it to have. Here is the panning function that I have currently:

//handles mouse panning, will pan while mouse left click if held down..
private int prevx = 0;
private int prevy = 0;
private void Mouse_Up(object sender, MouseEventArgs e)
{
    //reset previous x and y on mouse click up
    if (e.Button == MouseButtons.Left)
    {
        prevx = 0;
        prevy = 0;
    }
}
private void Mouse_Move(object sender, MouseEventArgs e, Chart chart)
{
    //if mouse was moved and mouse left click
    if (e.Button == MouseButtons.Left)
    {
        //a scalar, not entirely sure how useful it is, only works for my viewport
        double incr = 0.00130;

        if (prevx != 0 && prevy != 0)
        {
            //find x and y difference in mouse movement
            int diffx = e.X - prevx;
            int diffy = e.Y - prevy;

            //how much to increment x and y 
            double incrx = incr * Math.Abs(diffx);
            double incry = incr * Math.Abs(diffy);

            //get chart mins and maxs for both axes
            double minX = chart.ChartAreas[0].AxisX.Minimum;
            double maxX = chart.ChartAreas[0].AxisX.Maximum;
            double minY = chart.ChartAreas[0].AxisY.Minimum;
            double maxY = chart.ChartAreas[0].AxisY.Maximum;

            if (diffx > 0 && diffy > 0)
            {
                chart.ChartAreas[0].AxisX.Minimum -= (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum -= (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisY.Minimum += (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum += (maxY - minY) * incry;
                //Console.WriteLine("upleft");
            }
            else if (diffx < 0 && diffy > 0)
            {
                chart.ChartAreas[0].AxisX.Minimum += (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum += (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisY.Minimum += (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum += (maxY - minY) * incry;
                //Console.WriteLine("upright");
            }
            else if (diffx > 0 && diffy < 0)
            {
                chart.ChartAreas[0].AxisX.Minimum -= (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum -= (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisY.Minimum -= (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum -= (maxY - minY) * incry;
                //Console.WriteLine("bottom left");
            }
            else if (diffx < 0 && diffy < 0)
            {
                chart.ChartAreas[0].AxisX.Minimum += (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum += (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisY.Minimum -= (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum -= (maxY - minY) * incry;
                //Console.WriteLine("bottomright");
            }
            else if (diffx > 0 && diffy == 0)
            {
                chart.ChartAreas[0].AxisX.Minimum -= (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum -= (maxX - minX) * incrx;
                //Console.WriteLine("right");
            }
            else if (diffx < 0 && diffy == 0) 
            {
                chart.ChartAreas[0].AxisX.Minimum += (maxX - minX) * incrx;
                chart.ChartAreas[0].AxisX.Maximum += (maxX - minX) * incrx;
                //Console.WriteLine("left");
            }
            else if (diffy > 0 && diffx == 0)
            {
                chart.ChartAreas[0].AxisY.Minimum += (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum += (maxY - minY) * incry;
                //Console.WriteLine("down");
            }
            else if (diffy < 0 && diffx == 0) 
            {
                chart.ChartAreas[0].AxisY.Minimum -= (maxY - minY) * incry;
                chart.ChartAreas[0].AxisY.Maximum -= (maxY - minY) * incry;
                //Console.WriteLine("up");
            }
        }
        prevx = e.X;
        prevy = e.Y;
    }
}

I noticed there was a similar question, though it was in JavaFX, which I have no experience in. The panning feature that I envision is supposed to be able to move the mouse with the graph. So if I clicked on a certain point, dragged left 50px, that same point will still be directly under my mouse. In my current viewport, this works. However, if I make the graph smaller or larger, things don't work. When the graph becomes smaller, the graph moves slower than my mouse, and when the graph gets bigger, the graph moves faster than my mouse.

I know I should probably somehow include the viewport or chart width into the calculation, but I really just don't know how. Do any of you have experience with this? Thanks.

标签: c# mschart
1条回答
你好瞎i
2楼-- · 2019-06-14 02:58

You are quite right in doubting the 'scalar' approach to calculate values from pixels by a factor. This will never work and for more than one reason..

But the correct solution is so much simpler anyway. It makes use of one of the axis conversion functions to convert between data values and pixel positions. (There is also one to convert to and from percent positions, btw..)

I store the initial position to avoid rounding errors during the mouse move:

private double prevXMax = 0;
private double prevXMin = 0;
private double prevYMax = 0;
private double prevYMin = 0;
private Point mDown = Point.Empty;

All we need now is the Mousedown:

private void chart_MouseDown(object sender, MouseEventArgs e)
{
    //store previous data
    if (e.Button == MouseButtons.Left)
    {
        mDown = e.Location;
        prevXMax = chart.ChartAreas[0].AxisX.Maximum;
        prevXMin = chart.ChartAreas[0].AxisX.Minimum;
        prevYMax = chart.ChartAreas[0].AxisY.Maximum;
        prevYMin = chart.ChartAreas[0].AxisY.Minimum;
    }
}

And the much simpler MouseMove:

private void chart_MouseMove(object sender, MouseEventArgs e)
{
    Axis ax = chart.ChartAreas[0].AxisX;
    Axis ay = chart.ChartAreas[0].AxisY;

    //if mouse was moved and mouse left click
    if (e.Button == MouseButtons.Left)
    {
        double x0 = ax.PixelPositionToValue(mDown.X);
        double x1 = ax.PixelPositionToValue(e.X);
        double y0 = ay.PixelPositionToValue(mDown.Y);
        double y1 = ay.PixelPositionToValue(e.Y);

        ax.Minimum = prevXMin + (x0 - x1);
        ax.Maximum = prevXMax + (x0 - x1);
        ay.Minimum = prevYMin + (y0 - y1);
        ay.Maximum = prevYMax + (y0 - y1);
    }
}

One of the problems with a scaling calculation is that you may want to resize the chart; after that the scale will no longer work. Another is that you would have to take out the stuff outside of the ChartArea, and the InnerPlotPosition, i.e. the Axis.Labels, Titles and the Legend since the space they take shouldn't be scaled..

Note the the axis functions are only valid during Paintxxx or Mousexxx events..

Here is the result:

enter image description here

查看更多
登录 后发表回答