PictureBox 仅在调整大小时更新

PictureBox updates only on resize

正在尝试在表单应用程序上显示图表。 创建了一个 PictureBox 控件并使用其值初始化此 class。 在调整大小时,图表总是更新;在鼠标滚动上,它几乎没有。 它是 GraphBox 、 PictureBox 控件,位于 GraphBoxPanel 、 Panel 控件内。

这个class:

public struct DLT_measure_item
{
    public DateTime ts;
    public float value;
    public int id;
    public int X;
    public int Y;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct dlt_ser_meas
{
    public byte msg_id;         // 'D'
    public byte meas_count;        // Number of measures
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public byte[] port;        // Module ID (4b) + Port ID (4b)
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
    public float[] meas;       // measure
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
    public byte[] msg_end;
}

public class manageGraph
{
    private PictureBox box;
    public bool displayGrid = true;
    private int   horAxisMin_p = 0;
    private int   horAxisMax_p = 300;
    private float verAxisMin_p = 0;
    private float verAxisMax_p = 40;
    public int   horAxisMin
    {
        get { return this.horAxisMin_p; }
        set
        {
            if (value < horAxisMax_p)
            {
                this.horAxisMin_p = value;
                reDraw();
            }
        }
    }
    public int   horAxisMax      
    {
        get { return this.horAxisMax_p; }
        set
        {
            if (value > horAxisMin_p)
            {
                this.horAxisMax_p = value;
                reDraw();
            }
        }
    }
    public float verAxisMin
    {
        get { return this.verAxisMin_p; }
        set
        {
            if (value < verAxisMax_p)
            {
                this.verAxisMin_p = value;
                verPointPerUnit = graphArea.Height / (verAxisMax_p - this.verAxisMin_p);
            }
        }
    }
    public float verAxisMax
    {
        get { return this.verAxisMax_p; }
        set
        {
            if (value > verAxisMin_p)
            {
                this.verAxisMax_p = value;
                verPointPerUnit = graphArea.Height / (this.verAxisMax_p - verAxisMin_p);
            }
        }
    }
    Pen axes = new Pen(Color.Black, (float)1.5);
    public int horAxisSpacing = 30;
    public int verAxisSpacing = 20;
    public int horAxis = 20;
    public int verAxis = 20;
    private float horPointPerUnit = 1;
    private float verPointPerUnit = 1;
    public int horAxisTickLen = 5;
    public int verAxisTickLen = 5;
    public bool horAxisShowTime = false;
    private Rectangle graphArea = new Rectangle();


    public void reDraw()
    {
        box.Image.Dispose();
        Bitmap GraphBlankImage = new Bitmap(box.Width, box.Height);
        box.Image = GraphBlankImage;
        updatePointPerUnit();
        drawGrid();
        box.Refresh();

    }
    public manageGraph(PictureBox targetImageBoxbox)
    {
        box = targetImageBoxbox;
        horAxisMin_p = 0;
        horAxisMax_p = 300;
        verAxisMin_p = 0F;
        verAxisMax_p = 50F;
        updatePointPerUnit();
    }
    private Point measToPoint(DLT_measure_item measure) {
        Point coords = new Point();
        coords.X = graphArea.Width - (int)( 
         ((DateTime.Now - measure.ts).TotalSeconds  + horAxisMin_p) * horPointPerUnit  )   ;
        coords.Y = graphArea.Height - (int)(
         ((measure.value - verAxisMin_p) * verPointPerUnit));
        return coords;
    }
    public manageGraph(PictureBox targetImageBoxbox, 
                       int xmin, int xmax, float ymin, float ymax)
    {
        box = targetImageBoxbox;
        horAxisMin_p = xmin;
        horAxisMax_p = xmax;
        verAxisMin_p = ymin;
        verAxisMax_p = ymax;
        updatePointPerUnit();
    }

    private void updateGraphArea()
    {
        graphArea = new Rectangle(0, 0, box.Width - horAxis, box.Height - verAxis);
    }

    private void updatePointPerUnit()
    {
        updateGraphArea();
        horPointPerUnit = graphArea.Width / (horAxisMax_p - horAxisMin_p);
        verPointPerUnit = graphArea.Height / (verAxisMax_p - verAxisMin_p);
    }

    public void drawGrid()
    {

        //updatePointPerUnit();
        using (Graphics g = Graphics.FromImage(box.Image))
        {
            // X axis
            g.DrawLine(axes, graphArea.Left, graphArea.Bottom, box.Width, graphArea.Bottom);
            // Y axis
            g.DrawLine(axes, graphArea.Right + 1, graphArea.Top, graphArea.Right +1, graphArea.Bottom);
            using (Font ArialFont = new Font("Arial", 10))
            {
                g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
                // Put x labels 
                for (int i = 1; i <= (graphArea.Width / horPointPerUnit); i = i + (int)(horAxisSpacing / horPointPerUnit ) + 1)
                {
                    g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Width - ( i * horPointPerUnit) - 5, graphArea.Bottom +5);
                    g.DrawLine(axes, graphArea.Width - (i * horPointPerUnit), graphArea.Bottom + (horAxisTickLen / 2), graphArea.Width - (i * horPointPerUnit), graphArea.Bottom - (horAxisTickLen / 2));
                }
                // Put y labels 
                for (int i = 1; i <= (graphArea.Height / verPointPerUnit); i = i + (int)(verAxisSpacing / verPointPerUnit) +1)
                {
                    g.DrawString((i).ToString(), ArialFont, Brushes.Black, graphArea.Right + 1 , graphArea.Height - (i * verPointPerUnit) - 8);
                    g.DrawLine(axes, graphArea.Width - (verAxisTickLen / 2), (i * verPointPerUnit), graphArea.Width + (verAxisTickLen / 2), (i * verPointPerUnit));
                }
            }
            /*Put some random data*/
            DLT_measure_item testmeas = new DLT_measure_item();
            Point testGraphPoint = new Point();
            testmeas.ts = DateTime.Now;
            testmeas.value = 0;
            testGraphPoint = measToPoint(testmeas);
            g.FillEllipse(Brushes.Blue, testGraphPoint.X, testGraphPoint.Y, 4, 4);
            for (double i = 0; i < 300; i++)
            {
                double x = i;
                double freq = 10;
                double y = 30 - (i/10);
                testmeas.value = (float)y;
                testmeas.ts = DateTime.Now.AddSeconds(-1 * i);
                testGraphPoint = measToPoint(testmeas);
                g.FillEllipse(Brushes.Red, testGraphPoint.X, testGraphPoint.Y, 2,2);
            }
        }
    }
}

初始化:

    public DLThermalogMainForm()
    {
        InitializeComponent();
        Bitmap GraphBlankImage = new Bitmap(GraphBox.Width, GraphBox.Height);
        GraphBox.Image = GraphBlankImage;
        myGraph = new manageGraph(GraphBox);
        myGraph.drawGrid();

    }

那些处理者:

    private void Form1_ResizeEnd(object sender, EventArgs e)
    {
        myGraph.reDraw();
        OutputTextBox.AppendText("Resize." + Environment.NewLine);
    }

    private void GraphBox_MouseMove(object sender, MouseEventArgs e)
    {
        //Get the focus to have the wheel working
        GraphBoxPanel.Focus();
    }

    private void GraphBoxPanel_MouseWheel(object sender, MouseEventArgs e)
    {
        // Change x axis max value to zoom in/out the graph
        myGraph.horAxisMax += e.Delta/ 120;
        myGraph.reDraw();
    }

在调整大小事件中,它总是重绘;在鼠标滚轮上,只有较小的 horAxisMax 值才会快速执行(这有意义吗???),但对于较大的值,更新需要很多秒,或者根本不需要。 非常感谢

强制更新的最简单方法就是 invalidate the control

timer = new Timer();
timer.Interval = 200; //refreshes every 200 ms
timer.Tick += (sender,e) => targetImageBoxbox.Invalidate();
timer.Start();

像这样更改reDraw

public void reDraw()
{
    box.Image.Dispose();
    Bitmap GraphBlankImage = new Bitmap(box.ClientSize.Width, box.ClientSize.Height);
    updatePointPerUnit();
    drawGrid(GraphBlankImage);
    box.Image = GraphBlankImage;
}

drawGrid像这样:

public void drawGrid(Bitmap bmp)
{
    //updatePointPerUnit();  //??
    using (Graphics g = Graphics.FromImage(bmp))
    {
     ...
     ...
     ...
    }
}

现在带有网格的 Bitmap 应该会立即显示在 PictureBox 中。

如前所述,Graphics 对象是一种用于更改关联 Bitmap 的工具。要拿起它,Bitmap 应该分配给 PictureBoxeImage

另请注意,除非您的 PictureBox 没有 Border,否则 outer 大小(又名 Bounds ) 和 内部 尺寸 ClientRectangle / ClientSize。图片应该有 ClientSize

您可能想知道为什么您的原始代码不起作用?毕竟 Image 是一个引用类型,所以像你所做的那样改变它应该就足够了..

但深入 source code 我们发现了原因:

PictureBox 的图像是 属性 并且在其 setter 中调用了 InstallNewImage:

    public Image Image {
        get {
            return image;
        }
        set {
            InstallNewImage(value, ImageInstallationType.DirectlySpecified);
        }
    }

同样的调用也出现在其他几个地方,例如 LoadImageLocation 的 setter。但是仅在幕后更改 Image 不会强制 PictureBox 进行调用。 Refresh() 也应该这样做。而且,正如您发现的那样,调整它的大小也会导致 PictureBox 在图像位图中获取更改的数据。