C#中关于PANEL控件的Graphics绘图

2024-11-06 23:21:24
推荐回答(2个)
回答(1):

LZ关键问题是没有注意到图形在某位“位置”绘制后,如果该位置发生的移动,或其他图形遮挡了后会出现什么,如下图:

用其他什么东西,例如QQ遮挡 了一下原图形就没有了...因为在此之后没有人重新绘制了该图形,基于类似原因,即便你使用了Scoll,也无法看到后面的图形


你的程序我改造了一下,可以根据需求绘制图形,去掉Panel自带的HorizontalScroll,新增了一个hScrollBar(HorizontalScroll没有尝试成功,所以替换了,LZ可以多尝试下)

 private struct ImageContainerType
        {
            public Rectangle theImage;     // 图形容器--矩形
            public SolidBrush theBrush;    // 绘制该图形所用的画笔
        };
        // 绘制的图形的"容器"--这里用来存放绘制的矩形
        
        List imageList;

        // 用于控制绘图位置的全局变量
        int indexDraw = 0;

        private void button1_Click(object sender, EventArgs e)
        {
            /*
                调整思路
             * 方案A:静态绘图
             * 1、将所有绘制的图形保存到imageList容器中
             * 2、在需要时再执行绘制,例如拖动Scoll时触发
             * 缺点:如果绘制的图形较多,将消耗大量内存
             * 优点:只需要执行一次图形创建过程,以后随时可以使用,节省CPU或GPU资源
             *      且算法简单
             * 
             * 方案B:动态绘图
             * 1、先绘制当前所需要的图形(在可见区间内)
             * 2、在需要时如Scoll,重新(在可见区间内)绘制所需要的图形
             */
            int[] nums = new int[10000];

            // 初始化图形容器
            imageList = new List(100);
            ImageContainerType tempImage;

            Random random = new Random(); //随机数值
            for (int i = 0; i < 100; i++)
            {
                nums[i] = random.Next(0, 2);

            }
            for (int i = 0; i < 100; i++)
            {
                if (nums[i] == 1)
                {
                    SolidBrush r1 = new SolidBrush(Color.Red);//定义单色画刷    
                    Rectangle rect = new Rectangle(20 * i, 0, 10, 50);
                    //grap.FillRectangle(r1, rect);//填充这个矩形 
                    // 将当前图形存入容器
                    tempImage = new ImageContainerType();
                    tempImage.theBrush = new SolidBrush(Color.Red);
                    tempImage.theImage = new Rectangle(20 * i, 0, 10, 50);
                    imageList.Add(tempImage);
                }
                else
                {
                    SolidBrush b1 = new SolidBrush(Color.Blue);//定义单色画刷
                    Rectangle rect = new Rectangle(20 * i, 0, 10, 50);
                    //grap.FillRectangle(b1, rect);//填充这个矩形
                    tempImage = new ImageContainerType();
                    tempImage.theBrush = new SolidBrush(Color.Blue);
                    tempImage.theImage = new Rectangle(20 * i, 0, 10, 50);
                    imageList.Add(tempImage);
                }             
            }

            // [关键]设置Scorll的新值
            this.hScrollBar1.Maximum = 100;

            // 绘制全部图形
            DrawImage(0, imageList.Count - 1);

            MessageBox.Show("绘制完成");
        }
        /// 
        /// 实际的图形绘制方法
        /// 

        /// 所需绘制图形的起始编号
        /// 所需绘制图形的结束编号,如果end在start则绘制所有剩下图形
        void DrawImage(int start, int end)
        { 
            // 1、判定绘图容器是否存在
            if (imageList == null || imageList.Count <= 0)
            {
                MessageBox.Show("还没有生成图形");
                return;
            }

            // 2、绘图区间判定
            if (start >= imageList.Count || end >= imageList.Count)
            {
                MessageBox.Show("不在有效绘图区间");
                return;
            }

            // 3、
            if (start <= end)
            { 
                // 绘制从起始位置,到结束位置所有图形
                end = imageList.Count - 1;
            }

            // 4、绘制图形
            Graphics grap = pChart.CreateGraphics();
            grap.Clear(Color.White);
            Pen blue = new Pen(Color.Blue);
            Pen red = new Pen(Color.Red);
            for(int index=start;index<= end;index++)
            {
                grap.FillRectangle(imageList[index].theBrush, imageList[index].theImage);
            }            
        }

        /// 
        /// 在Scroll实际中重绘Panel内容
        /// 

        /// 
        /// 
        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            // 每次重绘"当前位置"直至结尾的所有图形
            DrawImage(e.NewValue, imageList.Count - 1);
        }

得到的效果是:

你可以看到,Scoll正确发挥了作用,但貌似图形不正确阿....前面的没有了?

原因在于现有的重绘访法是

// 每次重绘"当前位置"直至结尾的所有图形
DrawImage(e.NewValue, imageList.Count - 1);

而在imageList容器中存放的图形其坐标已经固定了,因此绘制结果也就可以预期了——这距离使用Scoll拖动图形距离不远了:>


其实关键就是图形和界面之间相对位置的处理了——界面是静止的,那么只有让图动起来,看我的改造:

注意上面两个图形的坐标,0-99和9~99,然后看相应的图形。的确界面没动,但图形动了,看起来就像是Panel向右滑动了10个单位一样。修改Scoll事件中的绘图方法就可以达到所需要的效果了

        /// 
        /// 在Scroll实际中重绘Panel内容
        /// 

        /// 
        /// 
        private void hScrollBar1_Scroll(object sender, ScrollEventArgs e)
        {
            int theValue = e.NewValue;
            int theNewImageX_Index = 0;


            // 要想实现“动态效果”的关键是重新计算图形与“静止”的界面
            // 之间的相对位置,即“界面不动”图形动(我不动那就麻烦你动一下了:>)
            // 这将消耗一定的CPU资源
            // --尝试将图形的位置根据当前"位置"进行移动
            // --将当前图形设定为"相对于界面"的第一个图形,后面的图形依次+1
            for (int index = theValue; index < imageList.Count; index++)
            {
                ImageContainerType temp = new ImageContainerType();
                temp.theImage = new Rectangle(20 * theNewImageX_Index, 0, 10, 50);
                temp.theBrush = imageList[index].theBrush;
                imageList[index] = temp;

                theNewImageX_Index++;
            }

            // 然后重新绘制图形
            DrawImage(theValue, imageList.Count - 1);
        }

 这里最关键的就是重新计算图形的坐标

new Rectangle(20 * theNewImageX_Index, 0, 10, 50);

回答(2):

private void panel1_Paint(object sender, PaintEventArgs e) { DrawMe(e.Graphics); }private void DrawMe(Graphics g){g.FillRectangle(blueBrush, x, y, width, height);...这里绘制 主意:绘制结束后不要使用 g.Dispone() 因为这个g来自控件绘制本身 不能销毁 }