需要帮助paint程序(Winform c#) [英] Need help with paint program (Winform c#)

查看:72
本文介绍了需要帮助paint程序(Winform c#)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用c#WinForms开发一个Paint程序。



问题:

屏幕仍在闪烁甚至在将DoubleBufered设置为true之后。



这是我的代码



I am developing a Paint Program in c# WinForms.

Problem :
Screen is still flickering even after setting DoubleBufered to true.

Here is my code

// Variables
 Bitmap pic;
 Graphics g;
 bool mouse_down = false;
 private void panel1_MouseDown(object sender, MouseEventArgs e)
 {
     mouse_down = true;
 }

 private void panel1_MouseUp(object sender, MouseEventArgs e)
 {
     mouse_down = false;
 }

 private void Form1_Load(object sender, EventArgs e)
 {
     pic = new Bitmap(panel1.Width, panel1.Height);
     g = Graphics.FromImage(pic);
 }

 private void panel1_MouseMove(object sender, MouseEventArgs e)
 {

     if (mouse_down)
     {
         panel1.Invalidate();
    g.FillRectangle(Brushes.Black,new Rectangle(e.X,e.Y,5,5));
     panel1.BackgroundImage = pic;
     }
 }





我知道这是因为panel1.Invalidate()。如果我注释掉这行代码,那么面板上只有一个矩形,并且在第二次尝试时没有任何内容。



如何解决问题?



任何形式的帮助都将受到赞赏。

推荐答案

停止所有无效 - 这是闪烁的原因。你正在以极高的频率重新绘制整个面板...

你的第二个问题是你试图处理你的方式图形对象...你创建一个图像并从那个Graphics对象然后分配它回到面板作为背景......这是一个完全浪费的恕我直言...

尝试直接绘制到面板的Graphics对象!!!

Stop all that Invalidate - it is the cause of the flickering. You are repainting the whole panel with an extreme frequency...
Your second problem is the way you try to handle you Graphics object...You create an image and from that a Graphics object and then assign it back to the panel as background...This is a total waste IMHO...
Try drawing directly to the Graphics object of the panel!!!
public partial class Form1 : Form
{
    Graphics g;
    bool mouse_down = false;

    public Form1 ( )
    {
        InitializeComponent ( );
    }

    private void panel1_MouseDown ( object sender, MouseEventArgs e )
    {
        mouse_down = true;
    }

    private void panel1_MouseMove ( object sender, MouseEventArgs e )
    {
        if ( mouse_down )
        {
            g.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
        }
    }

    private void panel1_MouseUp ( object sender, MouseEventArgs e )
    {
        mouse_down = false;
    }

    private void Form1_Load ( object sender, EventArgs e )
    {
        g = panel1.CreateGraphics ( );
    }
}







正如谢格尔说的那样,我的解决方案是非常笨拙的,它只能解决所提出的代码的直接问题,但是没有解决真正的问题 - 持久绘图!

这里解释问题一个非常简短的解释如何绘画在Windows中工作:

您的窗口(面板或其中任何一部分控件)正在绘制自己 - 意味着Windows不是Windows操作系统在窗口上绘制那些漂亮的边框和按钮而是窗口本身(和它的部分)知道如何画...

但是! Windows通过根据状态和/或可见性的变化向每个窗口发送绘制消息(WM_PAINT)来同步何时(和哪个部分)。例如,一个窗口变得可见(通过关闭它上面的窗口),Windows向它发送一条绘图消息,通知它有关该事件。

现在,你的窗口(面板)有很多在它上面绘制是从鼠标事件创建的,但是如果某些其他窗口重叠(隐藏)窗口的一部分会发生什么?那个隐藏的部分将会丢失,当你第二次看到窗口时 - 并收到一条油漆信息 - 没有人会知道如何重新创建它...

应该有两种主要方法坚持如何重新创建绘图的知识:

1.记录你在一种队列中所做的所有绘画,并在绘制消息时使用该队列再次绘制所有绘画。它对于更复杂的绘图程序非常有用,它可以单独处理每个绘制的对象(如圆形,矩形或线条)...

2.创建一个阴影副本 - 与主要部分断开连接窗口 - 你的绘画并将其复制回油漆信息



我现在将展示第二种方法的样本(因为它的样本更多):




As Sergey properly stated, my solution is very clumsy in a way that it's only solves the immediate problem of the presented code, but do not addresses the real problem - persistent drawing!
To explain the problem here a very short explanation of how painting in Windows works:
Your window (panel or any other control that part of it) is drawing itself - means it is not Windows the OS who paint those nice borders and buttons on your window but the window itself (and its parts) has the knowledge how to be painted...
But! The when (and what part) is synchronized by Windows via sending paint message (WM_PAINT) to each and every window according to changes in it state and/or visibility. For instance a window became visible (by closing the window on top of it) and Windows send it a paint message to notify it about the event.
Now, your window (the panel) has a lot of painting on it that was created from the mouse events, but what will happen if some other window overlaps (hides) a part of your window? That hidden part will be lost and when you window became visible a second time - and receive a paint message - no-one will know how to re-create it...
There should be two main methods to persists the knowledge of how to re-create the drawing:
1. Record all the paintings you made in a kind of queue and upon paint message use that queue to draw all the paintings again. It can be very useful for a more sophisticated paint program that can handle every painted 'object' (like circle, rectangle or line) individually...
2. Create a shadow copy - disconnected from the main window - of your painting and copy it back upon paint message

I will show a sample for the second approach (as it's much more sample) now:

public partial class Form1 : Form
{
    Graphics g;
    Graphics g_shadow;
    Bitmap bmp;
    bool mouse_down = false;

    public Form1 ( )
    {
        InitializeComponent ( );
    }

    private void panel1_MouseDown ( object sender, MouseEventArgs e )
    {
        mouse_down = true;
    }

    private void panel1_MouseMove ( object sender, MouseEventArgs e )
    {
        if ( mouse_down )
        {
            // draw both to the panel and to the shadow image
            g.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
            g_shadow.FillRectangle ( Brushes.Black, new Rectangle ( e.X, e.Y, 5, 5 ) );
        }
    }

    private void panel1_MouseUp ( object sender, MouseEventArgs e )
    {
        mouse_down = false;
    }

    private void Form1_Load ( object sender, EventArgs e )
    {
        g = panel1.CreateGraphics ( );
        // here create a second Graphics object that will paint to an non-visible shadow image
        bmp = new Bitmap ( panel1.Width, panel1.Height );
        g_shadow = Graphics.FromImage ( bmp );
    }

    private void panel1_Paint ( object sender, PaintEventArgs e )
    {
        // copy the shadow image to the main panel
        e.Graphics.DrawImageUnscaled ( bmp, e.ClipRectangle );
    }
}


我看不到你在哪里无效。你需要让布鲁诺·斯普雷彻(Bruno Sprecher)在评论中表示无效。



只有一个建议,这可能不是解决方案,但应该与您的整个设计一起工作:您不必使整个面板无效的。当用户执行绘画描边时,计算其范围并使仅使控件的部分无效。请参阅另外两个 System.Windows.Forms.Conrol.Invalidate 方法,那些接受 Rectangle Region 参数: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate%28v=vs.110%29.aspx [ ^ ]。







我忘记评论你的另一件有问题的作品了代码,使用 Graphics.FromImage 。因此,我不知道你的解决方案是如何运作的。您正在绘制位图,而不是屏幕上。屏幕上的渲染可能是在您未显示的代码的其他部分中完成的。并且可能存在真正的问题。



你看,分两步渲染,首先是在图像上,然后是屏幕上的图像,可以做得很好或纯粹。但如果你做得好,那将只是多余的双缓冲。此外,您可以以错误的方式执行此操作,然后您可以轻触该位图的区域。所以,我建议不要使用位图渲染并依赖双缓冲。



请参阅我过去的答案,解释正确的渲染和失效,第一个答案中有一些背景:

什么样的俏皮方法是Paint? (DataGridViewImageCell.Paint(...)) [ ^ ],

在面板上捕获绘图 [ ^ ],

mdi子表单之间的绘制线 [ ^ ]。



在你的情况下,这是最简单的无闪烁方法:在一些控件上渲染,覆盖 OnPaint 。这部分应该忽略笔画;相反,在控件上有一些数据模型和渲染模型数据。不要担心冗余渲染和性能: WM_PAINT 以一种狡猾的方式工作以优化事物。你的笔画不应该写在屏幕上,而是写在数据上;在笔划之后,您应该使笔划重新触摸的控件的一小部分无效。小心覆盖所有应该更改的区域。



在更复杂的方法中,立即在屏幕和数据上同时绘制。通过渲染失效将覆盖屏幕上的绘图。小心确保两个绘图阶段都一样。



-SA
I don't see where you invalidate what. You need to invalidate the was Bruno Sprecher advised in his comment.

Just one advise, which along may not be a solution, but should work together with your whole design: you don't have to invalidate the whole panel. When the user does the painting stroke, calculate its extents and invalidate only the part of control where you paint. Please see two other System.Windows.Forms.Conrol.Invalidate methods, those accepting a Rectangle or Region arguments: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.invalidate%28v=vs.110%29.aspx[^].



I forgot to comment on one more questionable piece in your code, using Graphics.FromImage. As a result, I don't know how you solution really works. You are drawing on a bitmap, not on screen. It's possible that rendering on screen is done in some other piece of the of the code you don't show. And there could be the real problem.

You see, rendering in two steps, first on an image and then the image on screen, could be done well or purely. But if you do it well, it would be just redundant double-buffering. Also, you can do it in a wrong way, then you could flicked on the area of this bitmap. So, I would advise not to use rendering of the bitmap at all and rely on double buffering.

Please see my past answers explaining proper rendering and invalidation, with some background in the first answer:
What kind of playful method is Paint? (DataGridViewImageCell.Paint(...))[^],
capture the drawing on a panel[^],
Drawing Lines between mdi child forms[^].

In your case, here is the simplest flicker-free approach: render on some control with overridden OnPaint. This part should ignore strokes; instead, have some data model and render model data on the control. Don't worry about redundant rendering and performance: WM_PAINT works in some cunning manner to optimize things. Your strokes should write not to screen, but too data; after the stroke, you should invalidate that small part of the control re-touched by the stroke. Be careful to cover all areas which should change.

In more sophisticated approach, draw immediately on screen and data at the same time. Drawing on the screen will be overwritten by rendering on invalidation. Be careful to ensure both phase of drawing do the same.

—SA


你需要在
Invalidate();
 in 

就是这样。



我在winform应用程序中添加了这个问题,解决了这个问题。

that's it.

I have solved the problem in my winform application by adding this in it.


这篇关于需要帮助paint程序(Winform c#)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆