Модераторы: feodorv, GremlinProg, xvr, Fixin
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> ScrollWindow в обработчике WM_SIZE, не очень приятное поведение 
V
    Опции темы
0x07L
Дата 24.7.2006, 07:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



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

Мое предположение основано на работе следующей программки:
Код

// Painting in WM_SIZE handler 2.cpp
//
// Возникает подозрение, что во время обработки сообщения WM_SIZE
// нельзя рисовать на вновь появившейся части окна.
// C FillRect однако все проходит нормально.
// Сейчас попробуем ScrollWindow.
//
// 24.07.2006

#include <windows.h>
#include <tchar.h>

const LPTSTR ClassName = _TEXT("Painting in WM_SIZE handler");
const LPTSTR MainWindowCaption = _TEXT("Painting in WM_SIZE handler");
const COLORREF ColorToFill = RGB(255, 69, 0);

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */, LPTSTR /* CmdLine */, int CmdShow)
{    
    WNDCLASS wc = {0};
    wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW + 1);
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = ClassName;

    RegisterClass(&wc);
    
    HWND hWindow = CreateWindow(
        MainWindowCaption, ClassName, WS_OVERLAPPEDWINDOW,
        100, 100, 100, 100,
        0, 0, 0, 0);

    ShowWindow(hWindow, CmdShow);

    MSG msg;
    while (true)
    {
        int a = GetMessage(&msg, 0, 0, 0);

        if (a == 0)
        {
            return static_cast<int>(msg.wParam);
        }

        if (a == -1)
        {
            return -1;
        }

        DispatchMessage(&msg);
    }
}

LRESULT CALLBACK WindowProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam)
{    
    static bool First = true;
    static int WindowHeight = 0;
    
    switch (Message)
    {
    case WM_SIZE:
        {
            RECT Rect;
            GetClientRect(hWindow, &Rect);

            if (Rect.bottom > WindowHeight && !First)
            {
                RECT NewRect = Rect;
                NewRect.bottom = WindowHeight;
                ScrollWindow(hWindow, 0, Rect.bottom - WindowHeight, &NewRect, 0);

                NewRect.top = WindowHeight;
                NewRect.bottom = Rect.bottom;
                ValidateRect(hWindow, &NewRect);
            }

            WindowHeight = Rect.bottom;
        }
        return 0;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hDC = BeginPaint(hWindow, &ps);

            if (First)
            {
                RECT Rect;
                GetClientRect(hWindow, &Rect);
                
                HBRUSH hBrush = CreateSolidBrush(ColorToFill);
                FillRect(hDC, &Rect, hBrush);
                DeleteObject(hBrush);

                First = false;
            }

            EndPaint(hWindow, &ps);
        }
        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWindow, Message, wParam, lParam);
}

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

Заметьте, что я применяю ValidateRect к вновь появившейся области. Это гарантирует, что по ней не пройдется реализация WM_ERASEBKGND в DefWindowProc.

PS Как тут можно exe-шник прикрепить?  

Это сообщение отредактировал(а) 0x07L - 24.7.2006, 07:20
PM MAIL   Вверх
Earnest
Дата 24.7.2006, 07:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

Репутация: 33
Всего: 183



Хм...
Попробуй делать ScrollWindow не прямо в  WM_SIZE, а после (пошли сам себе асинхронное сообщение). И еще, чтобы быть уверенным насчет EraseBackgnd, перехвати его сам, и ничего не делай. 
Файл можно прикрепить в полной форме ответа, или если будешь редактировать свой пост, там тоже будет поле для файла.  


--------------------
...
PM   Вверх
0x07L
Дата 24.7.2006, 08:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



WM_ERASEBKGND перехватил. Результаты те же.
А посылка асинхронного сообщения, пожалуй, действительно лучший способ решить эту проблему.

PS Интересно, почему FillRect действует, а ScrollWindow - нет.

Добавлено @ 08:30 
Кстати, пробовал также ставить ValidateRect перед ScrollWindow и вызывать перед ScrollWindow функцию UpdateWindow. Не помогает.  
PM MAIL   Вверх
Fixin
Дата 24.7.2006, 23:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ёжик
***


Профиль
Группа: Комодератор
Сообщений: 1357
Регистрация: 6.1.2004

Репутация: 8
Всего: 18



Код
case WM_GETMINMAXINFO:
        {    
            RECT Rect;    
            GetClientRect(hWindow, &Rect);    
            if (Rect.bottom > WindowHeight && !First)    
            {    
                RECT NewRect = Rect;    
                NewRect.bottom = WindowHeight;    
                ScrollWindow(hWindow, 0, Rect.bottom - WindowHeight, &NewRect, 0);
            }    
            WindowHeight = Rect.bottom;    
        }    
        return 0;
Тут суть в не в самом сообщении "гетминмаксинфо", а в том, что оно приходит до изменения размеров окна. 
PM MAIL ICQ   Вверх
0x07L
Дата 25.7.2006, 07:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Не пойдет. Во-первых, GetClientRect здесь возвращает старые координаты. Во-вторых, ScrollWindow не захочет двигать изображение в пока не видимую область. Это нужно делать в обработчике WM_SIZE (что не получается) или немного позже. 
PM MAIL   Вверх
Fixin
Дата 25.7.2006, 14:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ёжик
***


Профиль
Группа: Комодератор
Сообщений: 1357
Регистрация: 6.1.2004

Репутация: 8
Всего: 18



Цитата(0x07L @  25.7.2006,  07:07 Найти цитируемый пост)
Не пойдет.
А ты проверь. У меня-то пошло...
 
PM MAIL ICQ   Вверх
0x07L
Дата 8.8.2006, 16:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Я проверял. У меня не пошлО smile Значит, я чего-то не так сделал. Где здесь ошибка?
Код

// Painting in WM_SIZE handler 2.cpp
//
// Возникает подозрение, что во время обработки сообщения WM_SIZE
// нельзя рисовать на вновь появившейся части окна.
// C FillRect однако все проходит нормально.
// Сейчас попробуем ScrollWindow.
//
// 07.08.2006

#define _USE_METHOD_ 3
// 1 - standart method
// 2 - Fixin's method
// 3 - Earnest's method

#include <windows.h>
#include <tchar.h>

const LPTSTR ClassName = _TEXT("Painting in WM_SIZE handler");
const LPTSTR MainWindowCaption = _TEXT("Painting in WM_SIZE handler");
const COLORREF ColorToFill = RGB(255, 69, 0);

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR CmdLine, int CmdShow)
{    
    WNDCLASS wc = {0};
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = ClassName;

    RegisterClass(&wc);
    
    HWND hWindow = CreateWindow(
        MainWindowCaption, ClassName, WS_OVERLAPPEDWINDOW,
        100, 100, 100, 100,
        0, 0, 0, 0);

    ShowWindow(hWindow, CmdShow);
    UpdateWindow(hWindow);

    MSG msg;
    while (true)
    {
        int a = GetMessage(&msg, 0, 0, 0);

        if (a == 0)
        {
            return static_cast<int>(msg.wParam);
        }

        if (a == -1)
        {
            return -1;
        }

        DispatchMessage(&msg);
    }
}

LRESULT CALLBACK WindowProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam)
{    
    static bool First = true;
    static int WindowHeight = 0;
    
    switch (Message)
    {

    ///////////////////////////////////
    // STANDART METHOD

#if _USE_METHOD_ == 1

    case WM_SIZE:
        {
            // put a breakpoint here
            // ---------------------

            RECT Rect;
            GetClientRect(hWindow, &Rect);
            
            if (Rect.bottom > WindowHeight && !First)
            {
                RECT NewRect = Rect;
                NewRect.bottom = WindowHeight;
                
                ScrollWindow(hWindow, 0, Rect.bottom - WindowHeight, &NewRect, 0);
                            
                ValidateRect(hWindow, &Rect);
            }

            WindowHeight = Rect.bottom;
        }
        return 0;

#endif

    ///////////////////////////////////
    // FIXIN'S METHOD

#if _USE_METHOD_ == 2

    case WM_GETMINMAXINFO:
        {
            if (First)
            {
                return 0;
            }
            
            RECT Rect;
            GetClientRect(hWindow, &Rect);

            if (Rect.bottom > WindowHeight)
            {
                RECT NewRect = Rect;
                NewRect.bottom = WindowHeight;
                
                ScrollWindow(hWindow, 0, Rect.bottom - WindowHeight, &NewRect, 0);
            }
        }
        return 0;

    case WM_SIZE:
        {
            RECT Rect;
            GetClientRect(hWindow, &Rect);
            
            if (!First)
            {
                
                ValidateRect(hWindow, &Rect);
            }

            WindowHeight = Rect.bottom;
        }
        return 0;

#endif

    ///////////////////////////////////
    // EARNEST'S METHOD

#if _USE_METHOD_ == 3

    case WM_SIZE:
        {
            // put a breakpoint here
            // ---------------------

            RECT Rect;
            GetClientRect(hWindow, &Rect);
            
            if (Rect.bottom > WindowHeight && !First)
            {
                PostMessage(hWindow, WM_USER + 1, WindowHeight, 0);                            
                ValidateRect(hWindow, &Rect);
            }

            WindowHeight = Rect.bottom;
        }
        return 0;

    case WM_USER + 1:
        {
            RECT Rect;            
            GetClientRect(hWindow, &Rect);

            int Delta = static_cast<int>(Rect.bottom) - static_cast<int>(wParam);

            Rect.bottom = static_cast<long>(wParam);

            ScrollWindow(hWindow, 0, Delta, &Rect, 0);
        }
        return 0;

#endif

    ///////////////////////////////////

    case WM_ERASEBKGND:

        return TRUE;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hDC = BeginPaint(hWindow, &ps);

            if (First)
            {
                RECT Rect;
                GetClientRect(hWindow, &Rect);
                
                HBRUSH hBrush = CreateSolidBrush(ColorToFill);
                FillRect(hDC, &Rect, hBrush);
                DeleteObject(hBrush);

                First = false;
            }

            EndPaint(hWindow, &ps);
        }
        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWindow, Message, wParam, lParam);
}

PM MAIL   Вверх
Fixin
Дата 8.8.2006, 19:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ёжик
***


Профиль
Группа: Комодератор
Сообщений: 1357
Регистрация: 6.1.2004

Репутация: 8
Всего: 18



немножко изменил цикл сообщений, а остальное сам заметишь.
Код
#include <windows.h>
#include <tchar.h>

const LPTSTR ClassName = _TEXT("Painting in WM_SIZE handler");
const LPTSTR MainWindowCaption = _TEXT("Painting in WM_SIZE handler");
const COLORREF ColorToFill = RGB(255, 69, 0);

int APIENTRY _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int);
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR CmdLine, int CmdShow)
{    
    WNDCLASS wc = {0};
    wc.hCursor = LoadCursor(0, IDC_ARROW);
    wc.hInstance = hInstance;
    wc.lpfnWndProc = WindowProc;
    wc.lpszClassName = ClassName;

    RegisterClass(&wc);
    
    HWND hWindow = CreateWindow(
        MainWindowCaption, ClassName, WS_OVERLAPPEDWINDOW,
        100, 100, 100, 100,
        0, 0, 0, 0);

    ShowWindow(hWindow, CmdShow);
    UpdateWindow(hWindow);

    MSG msg;
    while (GetMessage(&msg, 0, 0, 0))        
        DispatchMessage(&msg);
    return static_cast<int>(msg.wParam);
}

LRESULT CALLBACK WindowProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam)
{    
    static bool First = true;
    static int WindowHeight = 0;
    
    switch (Message)
    {

    case WM_GETMINMAXINFO:
        {    
            RECT Rect;    
            GetClientRect(hWindow, &Rect);    
            if (Rect.bottom > WindowHeight && !First)    
            {    
                RECT NewRect = Rect;    
                NewRect.bottom = WindowHeight;    
                ScrollWindow(hWindow, 0, Rect.bottom - WindowHeight, &NewRect, 0);
            }    
            WindowHeight = Rect.bottom;    
        }    
        return 0;


    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hDC = BeginPaint(hWindow, &ps);

            if (First)
            {
                RECT Rect;
                GetClientRect(hWindow, &Rect);
                Rect.top+=2;
                
                HBRUSH hBrush = CreateSolidBrush(ColorToFill);
                FillRect(hDC, &Rect, hBrush);
                DeleteObject(hBrush);

                First = false;
            }

            EndPaint(hWindow, &ps);
        }
        return 0;

    case WM_DESTROY:

        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWindow, Message, wParam, lParam);
}

PM MAIL ICQ   Вверх
0x07L
Дата 8.8.2006, 21:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Все заметил. Я предвзято отнесся к коду в Вашем первом посте и не стал его просто копировать, справедливо полагая, что в таком виде, в каком он был приведен, он недееспособен, за что и поплатился smile Он работает, поскольку, когда размеры окна изменяет пользователь, после последнего сообщения WM_SIZE зачем-то приходит WM_GETMINMAXINFO, что вообще-то не следует из того, что написано в MSDN. Однако если изменить размер окна программно путем вызова SetWindowPos, такой WM_GETMINMAXINFO не приходит и Ваш способ не действует.

MSDN: "The WM_GETMINMAXINFO message is sent to a window when the size or position of the window is about to change".

Это сообщение отредактировал(а) 0x07L - 8.8.2006, 21:19
PM MAIL   Вверх
0x07L
Дата 8.8.2006, 21:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Мой код даже более недееспособен, согласен.

Спасибо, Fixin. Спасибо, Earnest.
Тема закрыта.
PM MAIL   Вверх
Fixin
Дата 8.8.2006, 21:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Ёжик
***


Профиль
Группа: Комодератор
Сообщений: 1357
Регистрация: 6.1.2004

Репутация: 8
Всего: 18



У тебя прямо талант находить глюки smile (это я вспомнил тему с ентером в листбоксе, кажется)
PM MAIL ICQ   Вверх
0x07L
Дата 8.8.2006, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 272
Регистрация: 10.6.2006
Где: Москва

Репутация: 4
Всего: 5



Это не талант. Просто редкое невезение  smile 
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0566 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.