почему бы не отправить WM_PAINT вручную

Я читал, что я никогда не должен отправлять WM_PAINT вручную и вместо этого должен вызывать InvalidateRect, но не нашел ничего о том, почему бы и нет. Так почему не?

обновление работает с InvalidateRect, но не с SendMessage(WM_PAINT)

LRESULT CALLBACK window_proc(HWND wnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
  switch (msg)
  {
    case WM_PAINT:
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(wnd, &ps);

        Polyline(..);

        EndPaint(wnd, &ps);
        return 0;

    case WM_USER:           
        // SendMessage(wnd, WM_PAINT, NULL, NULL);
        // InvalidateRect(wnd, NULL, FALSE);

        return 0;
  }
}

person Ivars    schedule 16.03.2014    source источник
comment
Где ты это прочитал? Тот, кто это написал, должен был включить объяснение. Подсказка: прочтите, что делает BeginPaint.   -  person Raymond Chen    schedule 16.03.2014
comment
@RaymondChen это было некоторое время назад ... это было что-то о том, что WM_PAINT должен переноситься системой. сегодня я попытался отправить WM_PAINT из сообщения WM_USER, и это не удалось. я имею в виду отсутствие эффекта. я добавлю свой код.   -  person Ivars    schedule 16.03.2014
comment
Неправильный код, как это, является основной причиной, по которой вам не следует отправлять WM_PAINT самостоятельно. Всегда отдавайте предпочтение InvalidateRect(), вызывайте UpdateWindow() после этого, если вам нужно, чтобы рисование было выполнено немедленно. Что должно быть довольно редко.   -  person Hans Passant    schedule 16.03.2014
comment
Если вы никогда не вызываете InvalidateRect, то BeginPaint говорит: "О, окно все еще действует". Ничего красить не надо.   -  person Raymond Chen    schedule 16.03.2014


Ответы (4)



Если вы хотите вызвать немедленную перерисовку, правильный подход заключается в следующем:

  1. Используйте InvalidateRect(), а затем UpdateWindow().

  2. Используйте RedrawWindow().

Это вызовет создание нового сообщения WM_PAINT.

person Remy Lebeau    schedule 16.03.2014

У вас нет никакой информации об окнах других программ, раскрывающих ваши окна. Эта информация есть только у операционной системы. Таким образом, вы не всегда знаете, когда и где ваше окно нужно перекрасить. WM_PAINT и BeginPaint предоставляют эту недостающую информацию.

person ScottMcP-MVP    schedule 16.03.2014
comment
Многие (большинство?) обработчиков WM_PAINT перерисовывают все свое окно, не глядя на недопустимую область, чтобы оптимизировать его, поэтому этот ответ на самом деле ничего не объясняет. - person Mark Ransom; 17.03.2014
comment
Независимо от того, использует программа недопустимую область или нет, она все равно не может узнать, когда что-то было обнаружено другими программами и нуждается в перерисовке. Таким образом, нужно обрабатывать WM_PAINT и вызывать BeginPaint в обработчике. - person ScottMcP-MVP; 17.03.2014
comment
Никто не спорит, что нужно обрабатывать WM_PAINT или вызывать BeginPaint. Вопрос в том, почему отправка WM_PAINT самому себе не работает, даже если она заканчивается в том же коде обработчика. Этот ответ не касается этого жизненно важного вопроса. - person Mark Ransom; 17.03.2014
comment
@MarkRansom Все элементы управления из общей библиотеки элементов управления перерисовывают только недействительную область. Поскольку большинство приложений полагаются на них, большинство приложений также получают это поведение бесплатно. Многие пользовательские обработчики WM_PAINT могут делать это неправильно, но это сравнительно редко. - person Luaan; 05.09.2019

Потому что WM_PAINT не настоящее сообщение.

Думайте об этом так, как будто каждое окно имеет структуру, хранящую «недопустимую область», то есть «эта часть окна на экране больше не актуальна и должна быть перерисована».

Эта недопустимая область изменяется самим оконным менеджером (размер окна изменяется, открывается и т. д.) или вызовами InvalidateRect, ValidateRect, EndPaint и т. д.

А теперь грубый макет того, как GetMessage справляется с этим:

... GetMessage(MSG* msg, ...)
{
  while(true) {
    if(ThereIsAnyMessageInTheMessageQueue()) {
      *msg = GetFirstMessageOfTheMessageQueue();
      return ...;
    } else if(TheInvalidRegionIsNotEmpty()) {
      *msg = CreateWMPaintMessage();
      return ...;
    } else {
      WaitUntilSomethingHappend();
    }
  }
}

tl;dr: WM_PAINT предназначен для получения, а не для отправки.

person Nicolas Repiquet    schedule 24.04.2015