BibhutiAlmighty Ответов: 3

Нужна помощь с программой paint (Winform c#)


Я разрабатываю программу Paint в c# WinForms.

Проблема :
Экран все еще мерцает даже после установки DoubleBufered в true.

Вот мой код

// 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.Аннулировать()". Если я закомментирую эту строку кода, то на панели появится только один прямоугольник, и со второй попытки ничего не будет нарисовано.

Как я могу решить эту проблему ?

Мы будем признательны за любую помощь.

0x01AA

Чего вы ожидаете, кроме мерцания? Подумайте о том, чтобы "аннулировать" каждое событие перемещения мыши.

Sergey Alexandrovich Kryukov

Я не вижу, где ОП делает недействительным, вот в чем проблема. И вы правы.
Есть еще кое-что, так что я отправляю ответ, Пожалуйста, смотрите. Я поверил твоему совету.
—СА

0x01AA

Большое Вам за это спасибо. Даже я думаю, что ты сделал 99,9999% работы.
Бруно

BibhutiAlmighty

Я погуглил о значении слова "ОП", но не смог его понять . Что это значит ?

Sergey Alexandrovich Kryukov

О, мне очень жаль... Я сам против аббревиатур, но использую эту, потому что обычная практика CodeProject, поэтому я принял ее, даже понимая, что это сбивает с толку. Я думаю, что это означает "оригинальный плакат" (автор оригинального поста), в данном случае вы. :-)
Извините за путаницу.
—СА

3 Ответов

Рейтинг:
1

Santosh Kokatnur

Вам нужно добавить

Invalidate();
в
form_resize event.
вот и все.

Я решил эту проблему в своем приложении winform, добавив в него это.


CHill60

Три года слишком поздно, и вы явно не читали другие решения и комментарии!
Придерживайтесь ответов на новые сообщения, где ОП все еще нуждается в помощи

Santosh Kokatnur

Ну, я просто ответил на него. Потому что я и раньше сталкивался с такой же проблемой. Так что я ответил.

Спасибо!

Рейтинг:
0

Kornfeld Eliyahu Peter

Прекратите все, что обесценивает - это причина мерцания. Вы перекрашиваете всю панель с чрезвычайной частотой...
Ваша вторая проблема заключается в том, как вы пытаетесь справиться с графическим объектом...Вы создаете изображение и из него графический объект, а затем назначаете его обратно на панель в качестве фона...Это полная трата времени, ИМХО...
Попробуйте нарисовать непосредственно графический объект панели!!!

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 OS рисует эти красивые границы и кнопки на вашем окне, а само окно (и его части) знает, как его рисовать...
Но! Когда (и какая часть) синхронизируется Windows с помощью отправки сообщения paint (WM_PAINT) в каждое окно в соответствии с изменениями его состояния и/или видимости. Например, окно стало видимым (закрыв окно поверх него), и Windows отправляет ему сообщение paint, чтобы уведомить его о событии.
Теперь на вашем окне (панели) есть много рисунков, созданных из событий мыши, но что произойдет, если какое-то другое окно перекрывает (скрывает) часть вашего окна? Эта скрытая часть будет потеряна, и когда ваше окно станет видимым во второй раз - и вы получите сообщение краски - никто не будет знать, как его воссоздать...
Должно быть два основных метода для сохранения знаний о том, как воссоздать чертеж:
1. Запишите все картины, которые вы сделали в своего рода очередь, и после получения сообщения paint используйте эту очередь, чтобы снова нарисовать все картины. Это может быть очень полезно для более сложной программы рисования, которая может обрабатывать каждый нарисованный "объект" (например, круг, прямоугольник или линию) индивидуально...
2. Создайте теневую копию - отключенную от главного окна - вашей картины и скопируйте ее обратно после сообщения paint

Я покажу образец для второго подхода (так как это гораздо больше образца) сейчас:
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 );
    }
}


Sergey Alexandrovich Kryukov

Мне очень жаль, но это просто неправильное решение. Он не сохранит результаты этих штрихов, потому что вы не используете OnPaint. Если вы используете его, вы должны были указать этот важный момент.

На самом деле, Invalidate не является источником мерцания, если вы используете его правильно. Можно сделать недействительной только часть сцены. А как насчет CreateGraphics? Да, вы можете использовать его, но гораздо более сложным способом. Один из способов таков: вы можете использовать оба экземпляра графики: один вы создаете, а другой передаете в OnPaint. Первый используется для рисования "здесь и сейчас", другой - для рендеринга после аннулирования. Это всего лишь идея. Но! простое рисование всего этого только в OnPaint будет работать, возможно, с более низкой производительностью.

Вы пробовали сами создать приложение для рисования? С первого взгляда то, что вы описали, не сработает. Это может сработать, если вы добавите гораздо больше деталей, но вы этого не сделали.

Спасибо за понимание.

—СА

Kornfeld Eliyahu Peter

Я не собирался создавать приложение paint, чтобы ОП только указывал на основные проблемы, которые я видел.
Вы правы насчет не сохранения части. Это решение не будет перекрашивать старую часть (скажем, перекрытую другим окном), а только с этого момента...Однако оригинал ОП тоже этого не сделал...
Вы также верно говорите о том, что Invalidate сам по себе не будет проблемой, но в том, как OP использует его...

Sergey Alexandrovich Kryukov

Я понимаю, но тебе нужно что-то с этим делать. Если кто-то непосредственно следует вашему коду, это не сработает.
Если вы понимаете, как исправить это так, как это было бы применимо к реальному приложению рисования, вам просто нужно это сделать.
—СА

Kornfeld Eliyahu Peter

Небольшое обновление - я взял этот код и создал проект, и Сюрприз-сюрприз...Это работает. Это работает в смысле сохранения рисунка. Несмотря на то, что вы (и, честно говоря, я тоже :-)) думали, что панель внутри формы сохраняет рисунки поперек изменения размера, перекрытия и перемещения...
Мой последний проект GUI был написан на C++ с использованием простых вызовов API, и от этого это как-то неожиданно...Мне придется рассмотреть его в деталях...

Sergey Alexandrovich Kryukov

Пожалуйста, поймите меня правильно. Если это для вас сюрприз, вы не должны публиковать его, пока не поймете, как он работает во всех деталях. И тогда вы должны предоставить всю информацию, а не только то, что вы написали. Как написано, это не сработает; это должно быть что-то другое. Пожалуйста, скажите мне: вы используете OnPaint? обработайте событие краски.

Если нет, то я не уверен, что он сохраняет рисунок. Сделайте это: переместите окно влево или вправо, за экран, чтобы покрыть место, где были штрихи. Затем переместите его назад, чтобы сделать штрихи видимыми. Они сохранились? Но если это так, то вы не предоставили существенную часть кода. Вам действительно нужно поместить эти стоксы в некоторые данные, сохранить эти данные и использовать эти данные для рендеринга при недействительности. Не обесценивая себя, вы скрываете проблему, но не решаете ее.

—СА

Kornfeld Eliyahu Peter

Я не стремлюсь доказать свою правоту или вашу неправоту - я только делюсь информацией (пока по крупицам)...
Я также ищу ваш вклад (чтобы сделать мою жизнь проще: -)), чтобы собрать как можно больше, когда (надеюсь, завтра) У меня будет время, чтобы изучить и обновить решение...

Sergey Alexandrovich Kryukov

Извините, но в данном случае этого недостаточно. Это даже не комментарий, это "решение", поэтому вам нужно быть более осторожным. Вам действительно нужно объяснить, как это может работать, и, возможно, добавить больше кода.
Таким образом, "исследовать и обновить решение" будет действительно хорошей идеей.
Пожалуйста, держите меня в курсе тоже.
—СА

Kornfeld Eliyahu Peter

Я расширил решение...

Sergey Alexandrovich Kryukov

Теперь есть объяснение, с объяснением фрагмента кода, но вы изменили реализацию и сделали ее ближе к OP.
Интересно: вы наблюдаете мерцание (я думаю, нет; это правда? ОП что-то напутал).
Тем не менее, есть проблема с этим: использование изображения в значительной степени избыточно, потому что используется двойная буферизация. Я думаю, вы видели мой ответ, где я объясняю это. Скажите, пожалуйста: используете ли вы изображение полного размера клиента (как-то), меньшего (что могло бы как-то оправдать использование image: performance)?
Так или иначе, я проголосовал за это решение, просто чтобы исключить свой первоначальный голос 1...
Спасибо,
—СА

Kornfeld Eliyahu Peter

Я сделал свое решение ближе к ОПУ, чтобы он лучше понял - в конце концов, это для него...
Конечно, никакого мерцания вообще нет - я думаю, что первоначальное мерцание произошло из-за того, что ОП инициировал полный размер недействительности с каждым рисунком...
Об изображении...Я создаю изображение того же размера, что и панель, используемая для рисования, но копирую только соответствующую часть в соответствии с недействительной областью, поэтому в очень редких случаях оно будет охватывать большие части рисунка...
Я проверил использование памяти и производительность и обнаружил, что все в порядке...
Спасибо Вам за ваше голосование и еще больше за ваши комментарии...
Также позвольте мне поздравить вас с MVP этого года (тоже) - молодец!

Sergey Alexandrovich Kryukov

- Спасибо, Питер. Мои поздравления для вас, слишком.

Я хотел увидеть ваше подтверждение. Как я и думал, никакого мерцания нет.
Однако использование изображения клиентского размера является чисто избыточным — оно просто удваивает функциональность двойной буферизации. Если вы включите его, мое решение, основанное на прямом рисовании OnPaint, также будет без мерцания и проще.

—СА

Kornfeld Eliyahu Peter

Спасибо - мой MVP был для меня сюрпризом, так как я вижу здесь людей с гораздо большими знаниями и способностью помочь...Я проскользнул туда случайно, но постараюсь изо всех сил оправдать ожидания...

О Мерцании изображений. Я уверен, что вы правы насчет двойной буферизации и комбинации OnPaint...Я думаю, что теперь два ответа (ваш и мой с исправлением) могут дать ОПУ всю картину, что-то узнать и выбрать свой путь к решению...

Sergey Alexandrovich Kryukov

Могу я объяснить, что вы делаете?

Некоторые говорят: "я использую эту и эту технику для кузнечной работы, но кувалда слишком тяжелая". Вы отвечаете: "просто снимите кувалду с процедуры, она будет выглядеть точно так же". (Или используйте деревянную кувалду.) Все в порядке, только металлическая часть не поддается ковке... :-)

—СА

BillWoodruff

+5, чтобы противостоять глупому голосованию вниз, и сказать, что мне нравится, как вы толкаете конверт здесь, и наслаждаться спиной и пеной между вами и Сергеем. Результат этого, я уверен, будет чем-то полезным для других людей в будущем.

Kornfeld Eliyahu Peter

Спасибо...

Sergey Alexandrovich Kryukov

Пожалуйста, смотрите мое обновление к моему ответу, после [EDIT], где я объясняю больше, особенно первую ссылку. Это может дать вам ключ к разгадке.
—СА

Рейтинг:
0

Sergey Alexandrovich Kryukov

Я не вижу, где вы делаете недействительным то, что. Вам нужно признать недействительным то, что посоветовал Бруно Шпрехер в своем комментарии.

Только один совет, который вместе с тем может и не быть решением, но должен работать вместе со всем вашим дизайном: вам не нужно аннулировать всю панель целиком Когда пользователь делает мазок картины, вычислите его экстенты и аннулируйте только часть контроля где ты рисуешь. Пожалуйста, посмотрите еще два 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 В результате я не знаю, как на самом деле работает ваше решение. Вы рисуете на растровом изображении, а не на экране. Вполне возможно, что рендеринг на экране выполняется в какой-то другой части кода, которую вы не показываете. И тут может возникнуть настоящая проблема.

Видите ли, рендеринг в два этапа, сначала на изображении, а затем на экране, может быть выполнен хорошо или чисто. Но если вы сделаете это хорошо, это будет просто избыточная двойная буферизация. Кроме того, вы можете сделать это неправильно, тогда вы можете щелкнуть по области этого растрового изображения. Поэтому я бы посоветовал вообще не использовать рендеринг растрового изображения и полагаться на двойную буферизацию.

Пожалуйста, смотрите мои прошлые ответы, объясняющие правильный рендеринг и недействительность, с некоторой предысторией в первом ответе:
Что же это за игривый метод-рисовать? (DataGridViewImageCell.Красить(...))[^],
захват рисунка на панели[^],
Рисование линий между дочерними формами mdi[^].

В вашем случае вот самый простой подход без мерцания: рендеринг на некотором элементе управления с переопределенным OnPaint Эта часть должна игнорировать штрихи; вместо этого имейте некоторую модель данных и визуализируйте данные модели на элементе управления. Не беспокойтесь о избыточном рендеринге и производительности: WM_PAINT работает каким-то хитрым образом, чтобы оптимизировать вещи. Ваши штрихи должны записывать не на экран, а слишком много данных; после штриха вы должны аннулировать ту небольшую часть элемента управления, которая была повторно затронута штрихом. Будьте осторожны, чтобы охватить все области, которые должны измениться.

При более сложном подходе рисуйте сразу на экране и данные одновременно. Рисунок на экране будет перезаписан рендерингом при признании недействительным. Будьте осторожны, чтобы убедиться, что обе фазы рисования делают то же самое.

—СА


BillWoodruff

+5 отчасти для того, чтобы противостоять глупому голосованию вниз, а главным образом для того, чтобы сказать, что я считаю этот совет разумным.

Sergey Alexandrovich Kryukov

Спасибо, Билл.
—СА

Mohamed Mitwalli

5+

Sergey Alexandrovich Kryukov

Спасибо, Мохаммед.
—СА