Как я вынуждаю окна NOT перерисовать что-нибудь в моем диалоговом окне, когда пользователь изменяет размер моего диалогового окна?

Когда пользователь захватывает угол окна изменяемого размера, и затем перемещает его, первые шаги окон содержание окна вокруг, затем выпускает WM_SIZE к изменяемому окну.

Таким образом, в диалоговом окне, где я хочу управлять перемещением различных дочерних элементов управления, и я хочу устранить мерцание, пользователь увидел в первый раз, какие окна OS думает, что окно будет похоже (потому что, AFAICT, ОС использует подход пересылки битовой строки к перемещающимся вещам в окне прежде, чем отправить WM_SIZE) - и только затем делает мое диалоговое окно, добираются, чтобы обработать перемещение его дочерних элементов управления вокруг или изменить размер их, и т.д., после которого это должно вызвать вещи перекрасить, который теперь вызывает мерцание (по крайней мере).

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

Я думал какое-то время, что это могло бы быть связано с CS_HREDRAW и флагами класса CSVREDRAW. Однако действительность - то, что я не хочу, чтобы ОС попросила, чтобы я стер окно - я просто хочу сделать перекрашивание меня без ОС, сначала изменяющей содержание моего окна (т.е. Я хочу, чтобы дисплей был тем, чем это было, прежде чем пользователь начал изменять размер - без любого bitblit'ing от ОС). И я не хочу, чтобы ОС сказала каждому управлению, что это должно быть перерисовано или (если это, оказалось, не было тем, которое было на самом деле затенено или показано изменением размер.

Что я действительно хочу:

  1. Переместить и изменить размер дочерних элементов управления, прежде чем что-либо будет обновлено на экране.
  2. Потяните все перемещенные или измененные дочерние элементы управления полностью так, чтобы они появились без артефактов в их новом размере и местоположении.
  3. Потяните промежуток пробелов дочерние элементы управления, не влияя на сами дочерние элементы управления.

Примечание: Шаги 2 и 3 могли быть инвертированы.

Вышеупомянутые три вещи, кажется, происходят правильно, когда я использую DeferSetWindowPos () в сочетании с диалоговым ресурсом, отмеченным как WS_CLIPCHILDREN.

Я извлек бы дополнительную небольшую пользу, если я мог бы сделать вышеупомянутое к DC памяти и затем только сделать единственную пересылку битовой строки в конце обработчика WM_SIZE.

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

  1. Я все еще не могу подавить Windows от выполнения 'прогнозирующей пересылки битовой строки'. Ответ: Посмотрите ниже для решения, которое переопределяет WM_NCCALCSIZE для отключения этого поведения.

  2. Я не вижу, как можно создать диалоговое окно, где его дочерние элементы управления тянут к двойному буферу. Ответ: См. ответ John's (отмеченный как ответ) ниже для того, как спросить Windows OS к двойному буферу Ваше диалоговое окно (примечание: это запрещает любой GetDC () промежуточные операции краски, согласно документам).


Мое Конечное решение (Благодарят Вас все, кто способствовал, особенно John K.):

После большого количества пота и слез, я нашел, что следующая техника работает безупречно, и в Аэро и в XP или с отключенным Аэро. Щелкание не существующее (1).

  1. Сцепите диалоговое окно proc.
  2. Переопределите WM_NCCALCSIZE, чтобы вынудить Windows проверить всю клиентскую область и не пересылку битовой строки что-либо.
  3. Переопределите WM_SIZE, чтобы сделать все Ваши перемещения, и изменяет размер использования BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos для всех видимых окон.
  4. Удостоверьтесь, что диалоговое окно имеет стиль WS_CLIPCHILDREN.
  5. Не используйте CS_HREDRAW|CS_VREDRAW (диалоговые окна не делают, так обычно не проблема).

Код расположения ваше дело - ее достаточно легкое для нахождения примеров на CodeGuru или CodeProject менеджеров по расположению, или к самокрутке.

Вот некоторые выборки кода, которые должны получить Вас большая часть пути:

LRESULT ResizeManager::WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch (msg)
    {
    case WM_ENTERSIZEMOVE:
        m_bResizeOrMove = true;
        break;

    case WM_NCCALCSIZE:
        // The WM_NCCALCSIZE idea was given to me by John Knoeller: 
        // see: http://stackoverflow.com/questions/2165759/how-do-i-force-windows-not-to-redraw-anything-in-my-dialog-when-the-user-is-resiz
        // 
        // The default implementation is to simply return zero (0).
        //
        // The MSDN docs indicate that this causes Windows to automatically move all of the child controls to follow the client's origin
        // and experience shows that it bitblts the window's contents before we get a WM_SIZE.
        // Hence, our child controls have been moved, everything has been painted at its new position, then we get a WM_SIZE.
        //
        // Instead, we calculate the correct client rect for our new size or position, and simply tell windows to preserve this (don't repaint it)
        // and then we execute a new layout of our child controls during the WM_SIZE handler, using DeferWindowPos to ensure that everything
        // is moved, sized, and drawn in one go, minimizing any potential flicker (it has to be drawn once, over the top at its new layout, at a minimum).
        //
        // It is important to note that we must move all controls.  We short-circuit the normal Windows logic that moves our child controls for us.
        //
        // Other notes:
        //  Simply zeroing out the source and destination client rectangles (rgrc[1] and rgrc[2]) simply causes Windows 
        //  to invalidate the entire client area, exacerbating the flicker problem.
        //
        //  If we return anything but zero (0), we absolutely must have set up rgrc[0] to be the correct client rect for the new size / location
        //  otherwise Windows sees our client rect as being equal to our proposed window rect, and from that point forward we're missing our non-client frame

        // only override this if we're handling a resize or move (I am currently unaware of how to distinguish between them)
        // though it may be adequate to test for wparam != 0, as we are
        if (bool bCalcValidRects = wparam && m_bResizeOrMove)
        {
            NCCALCSIZE_PARAMS * nccs_params = (NCCALCSIZE_PARAMS *)lparam;

            // ask the base implementation to compute the client coordinates from the window coordinates (destination rect)
            m_ResizeHook.BaseProc(hwnd, msg, FALSE, (LPARAM)&nccs_params->rgrc[0]);

            // make the source & target the same (don't bitblt anything)
            // NOTE: we need the target to be the entire new client rectangle, because we want windows to perceive it as being valid (not in need of painting)
            nccs_params->rgrc[1] = nccs_params->rgrc[2];

            // we need to ensure that we tell windows to preserve the client area we specified
            // if I read the docs correctly, then no bitblt should occur (at the very least, its a benign bitblt since it is from/to the same place)
            return WVR_ALIGNLEFT|WVR_ALIGNTOP;
        }
        break;

    case WM_SIZE:
        ASSERT(m_bResizeOrMove);
        Resize(hwnd, LOWORD(lparam), HIWORD(lparam));
        break;

    case WM_EXITSIZEMOVE:
        m_bResizeOrMove = false;
        break;
    }

    return m_ResizeHook.BaseProc(hwnd, msg, wparam, lparam);
}

Изменение размеров действительно сделано Изменением размер () участник, как так:

// execute the resizing of all controls
void ResizeManager::Resize(HWND hwnd, long cx, long cy)
{
    // defer the moves & resizes for all visible controls
    HDWP hdwp = BeginDeferWindowPos(m_resizables.size());
    ASSERT(hdwp);

    // reposition everything without doing any drawing!
    for (ResizeAgentVector::const_iterator it = m_resizables.begin(), end = m_resizables.end(); it != end; ++it)
        VERIFY(hdwp == it->Reposition(hdwp, cx, cy));

    // now, do all of the moves & resizes at once
    VERIFY(EndDeferWindowPos(hdwp));
}

И возможно заключительный хитрый бит виден в Репозиции ResizeAgent () обработчик:

HDWP ResizeManager::ResizeAgent::Reposition(HDWP hdwp, long cx, long cy) const
{
    // can't very well move things that no longer exist
    if (!IsWindow(hwndControl))
        return hdwp;

    // calculate our new rect
    const long left   = IsFloatLeft()   ? cx - offset.left    : offset.left;
    const long right  = IsFloatRight()  ? cx - offset.right   : offset.right;
    const long top    = IsFloatTop()    ? cy - offset.top     : offset.top;
    const long bottom = IsFloatBottom() ? cy - offset.bottom  : offset.bottom;

    // compute height & width
    const long width = right - left;
    const long height = bottom - top;

    // we can defer it only if it is visible
    if (IsWindowVisible(hwndControl))
        return ::DeferWindowPos(hdwp, hwndControl, NULL, left, top, width, height, SWP_NOZORDER|SWP_NOACTIVATE);

    // do it immediately for an invisible window
    MoveWindow(hwndControl, left, top, width, height, FALSE);

    // indicate that the defer operation should still be valid
    return hdwp;
}

'Хитрое' существо, которое мы стараемся не пытаться смешать с любыми окнами, которые были уничтожены, и мы не пытаемся задержать SetWindowPos против окна, которое не видимо (поскольку это документируется, поскольку "перестанет работать".

Я протестировал вышеупомянутое в реальном проекте, который скрывает некоторые средства управления и использует довольно сложные макеты с превосходным успехом. Существует нуль, мерцающий (1) даже без Аэро, даже когда Вы изменяете размер использования левого верхнего угла диалогового окна (большинство окон изменяемого размера покажет большую часть проблеска и проблем при захвате того дескриптора - IE, FireFox, и т.д.).

Если существует интерес достаточно, я мог бы быть убежден отредактировать свои результаты с реальной реализацией в качестве примера для CodeProject.com или где-нибудь подобный. Передайте меня.

(1) Обратите внимание на то, что невозможно избежать, чтобы каждый потянул поверх любого используемого, чтобы быть там. Для каждой части диалогового окна, которое не изменилось, пользователь ничего не видит (никакое мерцание вообще). Но где вещи изменились, существует изменение, видимое пользователю - этого невозможно избежать и является 100%-м решением.

32
задан Mordachai 3 February 2010 в 21:03
поделиться

7 ответов

Вы не можете предотвратить покраску во время изменения размера, но вы можете (с осторожностью) предотвратить Repping , где происходит мерцание. Во-первых, Bitblt.

Есть два способа остановить Bitblt вещь.

Если у вас есть класс окна верхнего уровня, то просто зарегистрируйте его с помощью CS_HREDRAW | CS_VREDRAW стили. Это приведет к изменению вашего окна, чтобы недействовать всей клиентской области, а не пытаться догадаться, какие биты не собираются изменить и бить.

Если у вас нет класса, но имейте возможность контролировать обработку сообщений (TRUE для большинства диалоговых ящиков). Обработка по умолчанию WM_NCCALCSIZE WM_NCCALCSIZE - это где класс стилей CS_HREDRAW и CS_VREDRAW обрабатывается поведение по умолчанию для возврата WVR_HREDRAW | WVR_VREDRAW От обработки WM_NCCALCSIZE, когда класс имеет CS_HREDRAW | CS_VREDRAW .

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

Вы можете слушать WM_ENTERSIZEMOVE и WM_EXITSIZEMOVE , чтобы знать при изменении размеров окна Вашего старта и остановки, и использовать, чтобы временно отключить или изменить способ рисунок и / или код макета Работает, чтобы минимизировать мигающую. Что именно вы хотите сделать, чтобы изменить этот код, будет зависеть от того, что ваш нормальный код обычно делает в WM_SIZE WM_PAINT и WM_ERASEBKGND .

Когда вы нарисуете фоном вашего диалогового окна, вам нужно не краска позади любой из детей. Убедившись, что диалог имеет WS_CLIPCHILDREL, так что у вас уже это обрабатывается.

Когда вы перемещаете дочерние окна, убедитесь, что вы используете PungindeferWindowPos / EndDefwindowpos, чтобы все перекраску происходило одновременно. В противном случае вы получите кучу мигания, так как каждое окно перерисовывает их нециркурную область на каждом вызове SetWindowPOS.

14
ответ дан 27 November 2019 в 21:15
поделиться

Если я правильно понял вопрос, это именно вопрос Raymond, адресованном сегодня .

4
ответ дан 27 November 2019 в 21:15
поделиться

есть только один способ эффективно диагностировать проблемы перерисовки - удаленная отладка.

Возьмите 2-й ПК. Установите на него MSVSMON. Добавьте этап сборки или утилиту, копирующую сборочные продукты на удаленный ПК.

Теперь вы должны быть в состоянии разместить точки останова в обработчиках WM_PAINT, WM_SIZE и т.д. и фактически проследить через диалоговый код по мере того, как он будет выполнять размер и перерисовку. Если вы загрузите символы с серверов символов MS, вы сможете увидеть полные стеки вызовов.

Некоторые хорошо размещенные точки останова - в ваших обработчиках WM_PAINT, WM_ERAGEBKGND, и вы должны иметь хорошее представление о том, почему ваше окно синхронно перекрашивается рано в течение цикла WM_SIZE.

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

0
ответ дан 27 November 2019 в 21:15
поделиться

Для некоторых элементов управления вы можете использовать сообщение WM_PRINT, чтобы преобразовать элемент управления в DC. Но на самом деле это не решает вашу основную проблему: вы хотите, чтобы Windows НЕ рисовала ничего во время изменения размера, а позволяла вам делать все это.

Ответ заключается в том, что вы просто не можете делать то, что хотите, пока у вас есть дочерние окна.

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

Это единственный известный мне способ полностью избавиться от мерцания и разрывов изображения во время операций изменения размера.

1
ответ дан 27 November 2019 в 21:15
поделиться

Если вы можете найти место для подключения его, CWND :: LockWindowUpdates () предотвратит возникновение любого чертежа до разблокировки обновлений.

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

Одним из них искать команды RedRaw, которые называются слишком часто во время изменения изменения размера. Если у вас элементы управления окнами вызывают RedRawindow () с указанным флагом RDW_UPDATENOW , он собирается перекрасить и там. Но вы можете распределить этот флаг и указывать rdw_invalidate вместо этого, который сообщает элемент управления недействительным окно без перекраски. Это будет перекрасить в простоях времени, сохраняя дисплей свежим без Spazzing.

0
ответ дан 27 November 2019 в 21:15
поделиться

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

Это происходит бесплатно в Vista Aero и выше, так что ваша боль может быть кратковременной.

Я не знаю общей реализации двойной буферизации для окон и системных элементов управления в XP, однако, вот некоторые вещи, которые стоит изучить:

Keith Rule's CMemDC для двойной буферизации всего, что вы рисуете сами с помощью GDI
WS_EX_COMPOSITED Window style (смотрите раздел примечаний, и что-то здесь на stackoverflow)

.
0
ответ дан 27 November 2019 в 21:15
поделиться

Что, по-видимому, работает:

  1. Используйте WS_CLIPCHILDREN в родительском диалоге (можно установить в WM_INITDialog)
  2. во время WM_SIZE, петля через дочерние элементы управления перемещением и изменением изменений их с использованием DefersetWindowPos ( ).

Это очень близко к идеальному, в моем тестировании под Windows 7 с Aero.

0
ответ дан 27 November 2019 в 21:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: