Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Принцип отрисовки окна (контролы - изображения)


Автор: ZC1989 28.4.2010, 17:33
Ребят, помогите с теорией, пожалуйста.

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

Для примера создал болванку и пробую в ней отрисовывать теже контролы, но столкнулся с глюкаму уже в самом начале(

вопрос первый: объясните в теории как правильно замещать и перерисовывать элементы окна что бы небыло глюков, тормозов и зависаний? понятно что фон диалога правильнее всего отрисовывать по WM_PAINT, а вот скажем кнопка, у неё есть 2 изображения (первое, когда она не активна, второе когда она нажата / на неё наведён курсор). такую кнопку надо отрисовывать то же по WM_PAINT и по действию над ней? 

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

код:
Код


#ifndef        __dialog_admin__
#define        __dialog_admin__

#include <windows.h>
#include ".\\resource.h"

extern HINSTANCE Global_hInst;


HBITMAP hb_BackGround;

HWND
    hIndexPage
    ;

void    Paint_bg(HWND hwnd, int IDDlgItem){
    BITMAP bm;
    RECT win_pos;
    HDC StaticHDC;
    HDC MemoryHDC;
    HWND hwndStatic = GetDlgItem(hwnd, IDDlgItem);
    StaticHDC = GetDC(hwndStatic);
    MemoryHDC = CreateCompatibleDC(StaticHDC);
    SelectObject(MemoryHDC, hb_BackGround);
    GetObject(hb_BackGround, sizeof(bm), &bm);
    GetWindowRect(hwnd, &win_pos);
    BitBlt(StaticHDC, win_pos.left, win_pos.top, 1000, 700, MemoryHDC, 0, 0, SRCCOPY);
    DeleteDC(MemoryHDC);
    ReleaseDC(hwndStatic,StaticHDC);    
}


BOOL    CALLBACK        ProcWinAdmin(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam){
    switch(Msg)
    {
    case WM_INITDIALOG:
        hb_BackGround = LoadBitmap(Global_hInst, MAKEINTRESOURCE(IDB_BACKGROUND));
        hIndexPage    = GetDlgItem(hwnd, IDC_INDEXPAGE);

            // Установление иконки.
        SendMessage(hwnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON))); 

            // Заголовок окна.
        SetWindowText(hwnd, "Fat-Ex CMS Admin SE");

            // Главное окно.
        MoveWindow(hwnd, (GetSystemMetrics(SM_CXSCREEN)-1000)/2, (GetSystemMetrics(SM_CYSCREEN)-700)/2, 1000, 700, 0);
            // Кнопка: Главная страница.
        MoveWindow(hIndexPage, 18, 123, 122, 16, 0);
        break;

    case WM_LBUTTONDOWN:
            /* Перетаскивание окна за любую точку. */
        ReleaseCapture();
        SendMessage(hwnd, WM_SYSCOMMAND, 0xF012, 0);
        break;

    case WM_PAINT:
        Paint_bg(hwnd, IDD_ADMIN);
        break;

    case WM_COMMAND:
    
        break;


    case WM_NOTIFY:
        
        break;


    case WM_CLOSE:
        EndDialog(hwnd, 0);
        break;
    }
    return FALSE;
}


#endif



вопрос третий: кнопка как видно из кода не перерисовывается, и получился то же странный эффект, если как я выше описал окно перетащить на пуск. то эта кнопка IDC_INDEXPAGE на время пропадает с формы...

с чём я ошибся?

Автор: Emura 28.4.2010, 22:17
Цитата

вопрос первый: объясните в теории как правильно замещать и перерисовывать элементы окна что бы небыло глюков, тормозов и зависаний? понятно что фон диалога правильнее всего отрисовывать по WM_PAINT, а вот скажем кнопка, у неё есть 2 изображения (первое, когда она не активна, второе когда она нажата / на неё наведён курсор). такую кнопку надо отрисовывать то же по WM_PAINT и по действию над ней? 

однозначно WM_PAINT.
Код

::PAINTSTRUCT ps;                        
::BeginPaint( HWND, &ps );
<рисуем>
::EndPaint( HWND, &ps );


или 

Код

ValidateRect(...)
<рисуем>


или
Код

::PAINTSTRUCT ps;                        
::BeginPaint( HWND, &ps );
::EndPaint( HWND, &ps );
<рисуем>



Цитата

то на нём остаются куски картинки

на нём - это на чем? скрин показывай.

Автор: GremlinProg 29.4.2010, 09:12
GetDC дает HDC в клиентской области, а GetWindowRect дает экранные координаты,
кроме того, я не понимаю, зачем пытаться что-то нарисовать по координатам диалога в устройстве статика,
да еще в ответ на сообщение WM_PAINT этого диалога

по приходу WM_PAINT, нужно рисовать именно на том окне, которому это сообщение пришло, и ни как иначе
(WM_PAINT посылается окну только для этих целей)

если хочешь рисовать на статике, который размещен на диалоге, дай ему стиль SS_OWNERDRAW и перехвати у диалога сообщение WM_DRAWITEM (там тебе будет HDC, на котором можно рисовать фон этого статика)

Цитата(Emura @  29.4.2010,  00:17 Найти цитируемый пост)

::PAINTSTRUCT ps;
::BeginPaint( HWND, &ps );
::EndPaint( HWND, &ps );
<рисуем>

этого не понял,
а вот с остальным - согласен, включая последний вопрос

Автор: GremlinProg 29.4.2010, 09:36
Цитата(ZC1989 @  28.4.2010,  19:33 Найти цитируемый пост)
а вот скажем кнопка, у неё есть 2 изображения (первое, когда она не активна, второе когда она нажата / на неё наведён курсор). такую кнопку надо отрисовывать то же по WM_PAINT и по действию над ней?

для стандартных компонент принцип зарисовки такой:
1. каждый компонент типа static, button, listbox, listview и т.п. имеют свои собственные процедуры окна
2. в каждой из этих процедур перехватывается WM_PAINT
3. если у компонента установлен спец. стиль, типа OWNERDRAW, то прежде, чем WM_PAINT будет обработано,
родительскому окну будет послано спец. уведомление, обычно это WM_DRAWITEM (или NM_CUSTOMDRAW, инкапсулированный в WM_NOTIFY)
4. с этим уведомлением будет послана любая служебная информация о том: что рисовать, где рисовать и какое состояние рисовать

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

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

Автор: GremlinProg 29.4.2010, 10:04
а помимо ручной отрисовки, некоторые компоненты могут сами рисовать простые изображения,
для этого, им достаточно послать в любое время соответствующие сообщения, например:
  кнопке - BM_SETIMAGE, BCM_SETIMAGELIST;
  статику - STM_SETICON, STM_SETIMAGE

Автор: ZC1989 29.4.2010, 12:25
GremlinProg, в смысле "пытаться чтото нарисовать поверх диалога"? Тоесть я неправильно рисую фон диалога?


вот примеры того как и что работает:

1. Окно только открыто, если его перетаскивать в рамках рабочего стола, то всё нормально.
http://www.dici-group.ru/__cpp/1/normal.JPG

2. я опустил окно так, что бы оно зашло за пуск и поднял обратно, на пуске остались куски картинки.
http://www.dici-group.ru/__cpp/1/bad.JPG

3. исходник
http://www.dici-group.ru/__cpp/1/ase_style.zip




кстати, на счёт того что окно лагает когда его переместить на пуск и обратно. Такое происходит когда окно создано как щас, когда я создал окно руками. то такого уже небыло...что это может быть?

Автор: GremlinProg 29.4.2010, 12:37
Цитата(ZC1989 @  29.4.2010,  14:25 Найти цитируемый пост)
в смысле "пытаться чтото нарисовать поверх диалога"?

Цитата(GremlinProg @  29.4.2010,  11:12 Найти цитируемый пост)
по координатам диалога


уже неважно, из картинок все понятно:

Цитата(ZC1989 @  28.4.2010,  19:33 Найти цитируемый пост)
HWND hwndStatic = GetDlgItem(hwnd, IDDlgItem);

Цитата(GetDlgItem Function @  MSDN)

If the function fails, the return value is NULL, indicating an invalid dialog box handle or a nonexistent control

т.е., у тебя hwndStatic равен NULL, поэтому, GetDC возвращает экранный HDC, а он не привязан к твоему окну,
т.е. он не будет перерисован в тех местах, откуда твое окно было сдвинуто

исправляй во первых IDDlgItem, который должен быть ассоциирован с конкретным компонентом на диалоге
во вторых - тот же GetWindowRect заменяй на GetClientRect ( это ошибка связана с первой ошибкой )
ну и в третьих, как увидишь результат, используй все же WM_DRAWITEM для такого рисования статика

Добавлено через 6 минут и 56 секунд
о, кстати, IDD_ADMIN - идентификатор самого диалога чтоли?

диалог - это не статик, дескриптор диалога искать не надо через GetDlgItem

если я правильно читаю мысли, то убирай из Paint_bg второй параметр и hwndStatic,
а hwnd - используй вместо hwndStatic

Добавлено через 10 минут и 20 секунд
Цитата

void    Paint_bg(HWND hwnd, int IDDlgItem){
    BITMAP bm;
    RECT win_pos;
    HDC StaticHDC;
    HDC MemoryHDC;
    HWND hwndStatic = GetDlgItem(hwnd, IDDlgItem);
    StaticHDC = GetDC(hwnd);
    MemoryHDC = CreateCompatibleDC(StaticHDC);
    SelectObject(MemoryHDC, hb_BackGround);
    GetObject(hb_BackGround, sizeof(bm), &bm);
    GetWindowRect(hwnd, &win_pos);
    GetClientRect(hwnd, &win_pos);
    BitBlt(StaticHDC, win_pos.left, win_pos.top, 1000, 700, MemoryHDC, 0, 0, SRCCOPY);
    DeleteDC(MemoryHDC);
    ReleaseDC(hwnd,StaticHDC);    
}

Автор: ZC1989 29.4.2010, 12:49
GremlinProg, так, я переделал функцию отрисовки

Код

void    Paint_bg(HWND hwnd){
    PAINTSTRUCT    sentinel;
    HBITMAP     hwBmp;
    HDC hdcTemp, bpHDC;
    BITMAP bm;

    bpHDC = BeginPaint(hwnd, &sentinel);
    hdcTemp    = CreateCompatibleDC(bpHDC);
    hwBmp = LoadBitmap(Global_hInst, MAKEINTRESOURCE(IDB_BACKGROUND));

    SelectObject(hdcTemp, static_cast<HGDIOBJ>(hwBmp));
    GetObject(static_cast<HGDIOBJ>(hwBmp), sizeof(bm), &bm);

    BitBlt(bpHDC, 0, 0, bm.bmWidth, bm.bmHeight, hdcTemp, 0, 0, SRCCOPY);
    DeleteDC(bpHDC);
    DeleteDC(hdcTemp);
    EndPaint(hwnd, &sentinel);
}


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

Автор: GremlinProg 29.4.2010, 12:54
тогда останется только правильно использовать Begin(End)Paint:
Цитата

void    Paint_bg(HWND hwnd){
    PAINTSTRUCT ps;
    BITMAP bm;
    RECT win_pos;
    HDC StaticHDC;
    HDC MemoryHDC;
    StaticHDC = GetDC(hwnd);
    StaticHDC = ::BeginPaint( hwnd, &ps );
    MemoryHDC = CreateCompatibleDC(StaticHDC);
    SelectObject(MemoryHDC, hb_BackGround);
    GetObject(hb_BackGround, sizeof(bm), &bm);
    GetClientRect(hwnd, &win_pos);
    BitBlt(StaticHDC, win_pos.left, win_pos.top, 1000, 700, MemoryHDC, 0, 0, SRCCOPY);
    DeleteDC(MemoryHDC);
    ReleaseDC(hwnd,StaticHDC);    
    ::EndPaint( hwnd, &ps );
}


Добавлено через 3 минуты и 1 секунду
Цитата(ZC1989 @  29.4.2010,  14:49 Найти цитируемый пост)
так, я переделал функцию отрисовки

угу
Цитата(ZC1989 @  29.4.2010,  14:49 Найти цитируемый пост)
если схватить окно за середину и поочереди перетащить во все углы экрана, так что бы прога скрывалась за пределами экрана, то окно перестаёт прорисовываться.

http://www.google.ru/search?q=gdi+leaks

Добавлено через 5 минут и 45 секунд
http://www.google.ru/search?q=gdi+leaks+task+manager

Автор: ZC1989 29.4.2010, 13:09
ерунда какая то, вот изменил функцию как ты показал, глюк тот же, окно перестаёт прорисовываться....

и на счёт WM_DRAWITEM

пытаюсь отрисовать кнопку
Код

case WM_DRAWITEM:
if(((DRAWITEMSTRUCT*)lParam)->hwndItem == hIndexPage){

    int y = 0;
    if(((DRAWITEMSTRUCT*)lParam)->itemState & ODS_SELECTED){
        y = 16;
    }else{
        y = 0;
    }

    HDC hdcTemp    = CreateCompatibleDC(((DRAWITEMSTRUCT*)lParam)->hDC);

    SelectObject(hdcTemp, hb_Controls);
             
    StretchBlt(
                        ((DRAWITEMSTRUCT*)lParam)->hDC, 
                        ((DRAWITEMSTRUCT*)lParam)->rcItem.left, 
                        ((DRAWITEMSTRUCT*)lParam)->rcItem.top, 
                        ((DRAWITEMSTRUCT*)lParam)->rcItem.right - ((DRAWITEMSTRUCT*)lParam)->rcItem.left, 
                        ((DRAWITEMSTRUCT*)lParam)->rcItem.bottom - ((DRAWITEMSTRUCT*)lParam)->rcItem.top, 
                        hdcTemp, 
                        0, 
                        y, 
                        127, 
                        16, 
                        SRCCOPY);
             
    DeleteDC(hdcTemp);

}
break;


это правильно?

Автор: GremlinProg 29.4.2010, 13:18
Цитата(ZC1989 @  29.4.2010,  15:09 Найти цитируемый пост)
окно перестаёт прорисовываться....

это не зависит от рисования,
gdi-leaks - утечки GDI-ресурсов, я же дал ссылки как их искать,

утечки - значит где-то распределил GDI-ресурс, а после использования не освободил,
ищи эти места и освобождай как полагается
Цитата(ZC1989 @  29.4.2010,  15:09 Найти цитируемый пост)
и на счёт WM_DRAWITEM

мы же уже выяснили, что компоненты у тебя ни какие не рисуются, т.е. WM_DRAWITEM не пригодился,
последний вид Paint_bg вполне работоспособен, здесь уже можно не копать

Автор: ZC1989 29.4.2010, 13:27
GremlinProg,  О! ты прав)) спасибо) я забыл очищать битман, загнал LoadBitmap и DeleteObject в функцию и лагать перестало))


только постой, почему WM_DRAWITEM не пригодился? я добавил кнопку которую то же перерисовываю, разве её не лучше как раз здесь и перерисовывать? 0.о

Добавлено через 5 минут и 30 секунд
и ещё вопрос в догонку: в WM_DRAWITEM у меня получается изменение вида кнопки только при нажатии, а можно ли тут отсладить находится ли курсор над кнопкой программным способом? не высчитывая координат мышки и расчётов? а то это кажется не больно рационально(

Автор: GremlinProg 29.4.2010, 14:54
а, кнопку, не заметил...
ну да, кнопку можно, только надо сначала ей включить стиль BS_OWNERDRAW

Цитата(ZC1989 @  29.4.2010,  15:27 Найти цитируемый пост)
можно ли тут отсладить находится ли курсор над кнопкой программным способом? не высчитывая координат мышки и расчётов?

это уже было в соседней теме: http://forum.vingrad.ru/forum/topic-298498/kw-отрисовка-кнопки-ownerdraw.html

Автор: ZC1989 30.4.2010, 16:06
опять столкнулся с проблемой: хотел сделать static с прозрачностью и белым цветом шрифта,но почему то неполучается задать никаких параметров.
пробовал отлавливать WM_COLORSTATIC, но оно вобще не приходит(

Автор: ZC1989 3.5.2010, 18:26
ерунда какая то у меня в программе получилась...
всё что отрисовываю (фон, кнопки, статики) всё нормально, но при обловлении окна (при скрытии и появлении контролов) они словно мерцают тоесть появляется сначала серый фон под ним, а потом уже перерисовывается...и если сдругим каким нибудь окном наехать на моё, а потом брать, оно то же не своевременно обновляется (появляется серый фон)

что может быть такое?


Автор: GremlinProg 4.5.2010, 09:02
Цитата(ZC1989 @  30.4.2010,  18:06 Найти цитируемый пост)
опять столкнулся с проблемой: хотел сделать static с прозрачностью и белым цветом шрифта,но почему то неполучается задать никаких параметров.пробовал отлавливать WM_COLORSTATIC, но оно вобще не приходит(

Цитата(WM_CTLCOLORSTATIC Notification @  MSDN)

A static control, or an edit control that is read-only or disabled, sends the WM_CTLCOLORSTATIC message to its parent window when the control is about to be drawn. By responding to this message, the parent window can use the specified device context handle to set the text and background colors of the static control. 

Цитата(ZC1989 @  3.5.2010,  20:26 Найти цитируемый пост)
ерунда какая то у меня в программе получилась...всё что отрисовываю (фон, кнопки, статики) всё нормально, но при обловлении окна (при скрытии и появлении контролов) они словно мерцают тоесть появляется сначала серый фон под ним, а потом уже перерисовывается...и если сдругим каким нибудь окном наехать на моё, а потом брать, оно то же не своевременно обновляется (появляется серый фон)что может быть такое?

мы много раз уже обсуждали эту тему:
1. заходишь в поиск: http://forum.vingrad.ru/act-Search/f-96.html
2. пишешь "мерцание"
3. нажимаешь "найти"

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)