Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.11.13;
Скачать: CL | DM;

Вниз

Ownerdraw Button Control и region ы   Найти похожие ветки 

 
Nick Denry ©   (2005-09-09 15:56) [0]

Народ подскажите , плиз, почему кнопка  (обычный "Button" class) не  изменяет  свою форму, если установить ей регион? Может имеет смысл ее сабклассировать, чтобы перехватывать какие то ее собщения?


 
Игорь Шевченко ©   (2005-09-09 16:29) [1]


> Народ подскажите , плиз, почему кнопка  (обычный "Button"
> class) не  изменяет  свою форму, если установить ей регион?


Изменяет


 
Nick Denry ©   (2005-09-09 16:46) [2]

Мой код:


Button1 := CreateWindow("BUTTON","_OK_", WS_CHILD or WS_VISIBLE, 10, 10, 70, 25, MainWnd, 0, HInstance, nil);

btnRgn := CreateEllipticRgn(10,10,30,25);

SetWindowRgn(Button1 ,  btnRgn, true);



Да, я согласен - регион изменяется (т.е. нельзя мышкой ткнуть в какой либо угол кнопки, чтобы пришло WM_COMMAND), но ее внешний вид остается неизменным, и даже если она OWNERDRAW  - то область осечения - не становится прозрачной - а выводится белым.

Игорь, я знаю что я туплю порой, неужели и в этот раз? :)


 
Nick Denry ©   (2005-09-09 16:49) [3]

Я думал - может Parent - окно не перерисовывается, но оно вроде как перерисовывается...

прочем чего мелочиться - вот весь код:

Program test;

uses
 windows,
 messages;

var
 wc : TWndClassEx;  //Переменная шаблона класса окна
 MainWnd : HWND;   //Описатель главного окна
 Mesg : TMsg;          //Переменная для цикла сбора сообщений

 Button1 : HWND;

 btnRgn : HRGN;

 bmpUp, bmpDwn : HBITMAP;

function GetRootDir : String;
var
  i : Integer;
  lpszExeDirPath : string;
begin
lpszExeDirPath := ParamStr(0);
for i := Length(lpszExeDirPath) downto 0 do
   begin
     if lpszExeDirPath[i] = "\" then begin
      SetLength(lpszExeDirPath,i);
      break;
     end;
   end;
   Result := lpszExeDirPath;
end;

function WindowProc(wnd:HWND; Msg : Integer; Wparam:Wparam; Lparam:Lparam):Lresult;stdcall;
var
   KS : TKeyboardState;
   TDC : HDC;

Begin
 {Далее происходит цикл обработки сообщений}
 case msg of
 WM_CREATE:
           begin
             bmpUp := LoadImage(0, PChar(GetRootDir+"ups.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
           end;
 WM_KEYDOWN:
            begin
                 if wParam = VK_SHIFT
                 then begin
                      if (GetKeyState(VK_TAB)=1) then
                      mEssageBox(0,0,0,MB_OK);
                 end;
            end;

 WM_DRAWITEM:
             begin
               TDC := CreateCompatibleDC(0);
               SelectObject(TDC, bmpUp);
               SetBkMode(PDRAWITEMSTRUCT(Lparam)^.hDC, TRANSPARENT);
               BitBlt(PDRAWITEMSTRUCT(Lparam)^.hDC,0,0,70,25,TDC, 0,0,SRCCOPY);
               DeleteDC(TDC)
             end;
 WM_LBUTTONDOWN:
                 begin
                   Rectangle(GetWindowDC(wnd),80,80, 120,120);
                 end;    
 WM_DESTROY:
            begin
              DeleteObject(bmpUp);
              PostQuitMessage(0);
              Result:=0;
              Exit;
            end;
  else Result:=DefWindowProc(wnd,msg,wparam,lparam);
 end;
End;

{Переменные xPos,yPos,nWidth,nHeight в принципе не нужны, однако я их завел
(и вам рекомендую) для лучшей читабельности программы}

var
 xPos,yPos,nWidth,nHeight : Integer;

begin //Тело программы
{  Далее идет заполнение шаблона класса окна}

wc.cbSize:=sizeof(wc);
wc.style:=cs_hredraw or cs_vredraw;
wc.lpfnWndProc:=@WindowProc;
wc.cbClsExtra:=0;

wc.cbWndExtra:=0;
wc.hInstance:=HInstance;
wc.hIcon:=LoadIcon(0,idi_application);
wc.hCursor:=LoadCursor(0,idc_arrow);
wc.hbrBackground:=COLOR_BTNFACE+1;
wc.lpszMenuName:=nil;
wc.lpszClassName:="WinMin : Main";
RegisterClassEx(wc); //Регистрация нового класса в системе

{"заполнение переменных xPos,yPos,nWidth,nHeight}

xPos:=100;
yPos:=150;
nWidth:=400;

nHeight:=250;

{ Создание главного окна}
MainWnd:=CreateWindowEx (
0,                    //флаги расширенных стилей
"WinMin : Main",    //имя класса окна, данное при заполнении структуры wc
"Win Min",          //заголовок окна
ws_overlappedwindow, //флаги стилей окна
{подробнdее о стилях см. после текста программы}
xPos,               //горизонтальная позиция окна
yPos,               //вертикальная позиция окна

nWidth,             //ширина окна
nHeight,            //высота окна
0,                  //описатель родительского окна (parent) или окна-владельца (owner)
0,                  //описатель меню окна (меню нет, нет и описателя)
Hinstance,          //описатель приложения
nil                 //address of window-creation data
);

Button1 := CreateWindow("BUTTON","_OK_", WS_CHILD or WS_VISIBLE or BS_OWNERDRAW, 10, 10, 70, 25, MainWnd, 0, HInstance, nil);

btnRgn := CreateEllipticRgn(10,10,30,25);

SetWindowRgn(Button1 ,  btnRgn, true);

ShowWindow(MainWnd,CmdShow); //Отображаем окно

While GetMessage(Mesg,0,0,0) do
begin
 TranslateMessage(Mesg);
 DispatchMessage(Mesg);
end;
end.


 
Игорь Шевченко ©   (2005-09-09 17:07) [4]

"The coordinates of a window"s window region are relative to the upper-left corner of the window, not the client area of the window. "


 
Nick Denry ©   (2005-09-09 17:15) [5]

Button1 := CreateWindow("BUTTON","_OK_", WS_CHILD or WS_VISIBLE or BS_OWNERDRAW, 10, 10, 70, 25, MainWnd, 0, HInstance, nil);

btnRgn := CreateEllipticRgn(10,10,30,25);

??

Игорь, объясни плиз в чем тут клиентские и оконные координаты?

имхо же все в оконных идет?


 
Игорь Шевченко ©   (2005-09-09 17:26) [6]

Nick Denry ©   (09.09.05 17:15) [5]


> Игорь, объясни плиз в чем тут клиентские и оконные координаты?


Вообще-то оконная координата левого верхнего угла окна равна нулю.


 
Nick Denry ©   (2005-09-09 18:30) [7]

Т.е. ты хочешь сказать, что регион должен создаваться как

btnRgn := CreateEllipticRgn(0,0,20,15);

?


 
Игорь Шевченко ©   (2005-09-09 18:32) [8]

Nick Denry ©   (09.09.05 18:30) [7]


> Т.е. ты хочешь сказать, что регион должен создаваться как
>


Вообще-то да :)


 
Nick Denry ©   (2005-09-09 19:22) [9]

Отсечения воспроизведения все равно не происходит.

Мне кажется, что

===
int SetWindowRgn(
   HWND hWnd,
   HRGN hRgn,
   BOOL bRedraw // window redraw flag
  );
----
Remarks

If the bRedraw parameter is TRUE, the system sends the WM_WINDOWPOSCHANGING and WM_WINDOWPOSCHANGED messages to the window.
====

у кнопок на 2 этих сообщения какая-то нестандартная обработка...


 
y-soft ©   (2005-09-09 20:08) [10]

>Nick Denry ©   (09.09.05 19:22) [9]

Все рисуется и обрабатывается (пример из книги Марко Канту, Тим Гуч с Джоном Ф. Лэм "Delphi. Руководство разработчика"):

http://y-soft.comsignal.ru/Delphi/DDHROUND_PAS.HTM


 
Nick Denry ©   (2005-09-09 22:09) [11]

Да , но с виндовой кнопкой почему-то не работает


 
Турист   (2005-09-09 23:59) [12]

Наверно, это все-таки неправильно


procedure TForm1.Button2Click(Sender: TObject);
var
 Button: Hwnd;
begin
 Button := CreateWindow("BUTTON","_OK_", WS_POPUP,
                        10, 10, 70, 25, 0, 0, HInstance, nil);
 SetWindowRgn(Button, CreateEllipticRgn(0, 0, 70, 25), False);
 Windows.SetParent(Button, Handle);
 ShowWindow(Button, SW_SHOW)
end;


 
Nick Denry ©   (2005-09-10 12:01) [13]

Да, последний вариант действительно работает...

интресно, в чем все-таки разница? :)


 
Турист   (2005-09-10 12:37) [14]

>Nick Denry ©   (10.09.05 12:01) [13]
>интресно, в чем все-таки разница? :)
Между чем ?


 
Nick Denry ©   (2005-09-13 00:21) [15]

Ну теперь собственно говоря между WS_POPUP +   Windows.SetParent(Button, Handle); и WS_CHILD;


 
Nick Denry ©   (2005-09-13 00:23) [16]

правда я с испугу свой контрол реализовал в Dll и CS_GLOBALCLASS :)

Впринципе, получилось даже интереснесней - поддержка 4 состояний и PNG :)


 
Игорь Шевченко ©   (2005-09-13 10:13) [17]

Nick Denry ©   (13.09.05 00:21) [15]


> Ну теперь собственно говоря между WS_POPUP +   Windows.SetParent(Button,
> Handle); и WS_CHILD;


Все решается очень просто, при создании окна кнопки добавь ей стиль WS_CLIPSIBLINGS.

Вот такой пример работает:

procedure TForm1.Button1Click(Sender: TObject);
var
 ButtonRect: TRect;
begin
 FButton := CreateWindowEx(0, "BUTTON","_OK_",
   WS_CHILD or WS_VISIBLE or WS_CLIPSIBLINGS or BS_PUSHBUTTON,
   10, 10, 70, 25, Handle, 0, HInstance, nil);
 Windows.GetWindowRect(FButton, ButtonRect);
 OffsetRect(ButtonRect, -ButtonRect.Left, -ButtonRect.Top);
 FButtonRgn := CreateEllipticRgnIndirect (ButtonRect);
 Win32Check((FButtonRgn <> 0) and (SetWindowRgn (FButton, FButtonRgn, true) <> 0));
end;


Без этого стиля придется каждый раз при перерисовке вызывать функции InvalidateRect у родительского окна и UpdateWindow у дочернего.

А ларчик открывается просто: создаешь обычную кнопку и winapi"шную кнопку, смотришь Spy++, чем окна отличаются.


 
Игорь Шевченко ©   (2005-09-13 10:14) [18]

Удалено модератором
Примечание: Дубль


 
Nick Denry ©   (2005-09-13 13:03) [19]

2Игорь Шевченко ©   (13.09.05 10:13) [17]

Ага, вот теперь можно кричать, размахивать руками и говорить, я так и знал, я так и думал... :)

Я вот только теперь чего думаю, обратно на кнопки перейти и свой контрол выкинуть нафиг или не мучаться с суперклассированием?


 
Игорь Шевченко ©   (2005-09-13 13:20) [20]

Nick Denry ©   (13.09.05 13:03) [19]


> Я вот только теперь чего думаю, обратно на кнопки перейти
>


А я не знаю, что тебе требуется. Как назначить регион окну кнопки, вроде разобрались, причем тут саб/суперклассинг, я не совсем понимаю.


 
Nick Denry ©   (2005-09-13 13:42) [21]

Требуется компонент (не в дельфийском понятии этого слова :),

кнопка, имеющая след. параметры:

Графическа кнопка произвольной формы (задется маской), имеет четыре (т.е. три состояния, на каждое из которых отрисовывается своя картинка) -

1. Кнопка отжата (1ая картинка)
2. Кнопка нажата (2ая картика)
3. Мышь над (сотсюда суперклассинг, сообщние WM_MOUSEOVER, 3я картинка)

Так же при нажатии/отжатии/наведении кнопка менят цвет надписи, на 3 заданных.

+ Возможность назначить Шрифт, которым будет отрисовывться текст на кнопке.

Кнопка должна быть расположенна в множстве диалогов программы (в ресурсах, отсюда и CS_GLOBALclASS) и не должн быть размазанна по всму коду (т.е. код кнопки в проекте должен встречаться один раз (т.е. вроде как Dll)- в остльных модулях только создание, уничтожение и назначение свойств (я сделал через SetProp/GeProp).

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

Я не знаю, насколько использовние контролов (о, слово наконец-то вспомнил!) со стилем CS_GLOBALCLASS оправданно и безопасно....


 
Nick Denry ©   (2005-09-13 13:53) [22]

Игорь, а что такое
Win32Check((FButtonRgn <> 0) and (SetWindowRgn (FButton, FButtonRgn, true) <> 0));

Я первый раз такую конструкцию вижу...


 
Игорь Шевченко ©   (2005-09-13 14:37) [23]

Nick Denry ©   (13.09.05 13:42) [21]


> Требуется компонент (не в дельфийском понятии этого слова
> :),


Это называется Control. Делай и воздастся тебе.


> Я не знаю, насколько использовние контролов (о, слово наконец-то
> вспомнил!) со стилем CS_GLOBALCLASS оправданно и безопасно....


И оправдано и безопасно. Располагается он в DLL, также, как и Common Controls, на этапе инициализации DLL класс регистрируется.

Nick Denry ©   (13.09.05 13:53) [22]


> Win32Check((FButtonRgn <> 0) and (SetWindowRgn (FButton,
> FButtonRgn, true) <> 0));


if not ((FButtonRgn <> 0) and (SetWindowRgn (FButton,
> FButtonRgn, true) <> 0)) then
 raise Exception.Create (SysErrorMessage(GetLastError));

одно и то же.


 
Nick Denry ©   (2005-09-13 19:01) [24]

на этапе инициализации DLL класс регистрируется

Т.е. мне надо сделать что-то типа функции initCommonControls?

Или достаточно при DLL_PROCESS_ATTACH зарегистрировать этот класс?

И еще, для назначения контролу индивидуальных параметров я использую SetProp/GetProp,

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

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

У меня сначала было так:

{код приложения (примерно, пишу не из дома), использующего длл, функция WndProc}


...(Глобальные переенные)

var
  controlDll: THandle;
  graphButton : HWND;

WM_CREATE:
         begin
         
          controlDll := LoadLibrary("libname.dll");
          graphButton := CreateWindow("antGraphBtnClass", ...);
          SetProp(graphButton, "image_up", hBitmap_handle);
          {ну и т.д.}
         end;

А вот когда я на
WM_DESTROY:
          begin
{             Освобождаю библиотеку}
            FreeLibrary (controlDll); - то прога вызывает исключение, т.к. я так понимаю еще обращается к процедуре окна самого контрола, а тут библиотека как бы убирается...

          end;

так вот когда ее можно нормально освободить - после цикла

while GetMessage ???


 
Игорь Шевченко ©   (2005-09-14 10:11) [25]


> А вот когда я на
> WM_DESTROY:
>           begin
> {             Освобождаю библиотеку}
>             FreeLibrary (controlDll); - то прога вызывает
> исключение, т.к. я так понимаю еще обращается к процедуре
> окна самого контрола, а тут библиотека как бы убирается.
> ..
>
>           end;


Во-первых, зачем библиотеку освобождать ?
Во-вторых, вызывается ли UnregisterClass в библиотеке при ее освобождении
В-третьих,  и самое главное:

ПОЧЕМУ ЛЮДИ НИКОГДА НЕ ПИШУТ ДЕТАЛЬНОЕ ОПИСАНИЕ ПРОБЛЕМЫ, НАПРИМЕР, КАКОЕ ИСКЛЮЧЕНИЕ У НИХ ВОЗНИКАЕТ ? У ОТВЕЧАЮЩИХ ЧАЩЕ ВСЕГО НЕТ ПЕРЕД ГЛАЗАМИ ЭКРАНА АВТОРА ВОПРОСА.

ЕСЛИ КТО ИЗ АВТОРОВ ВОПРОСОВ СЧИТАЕТ СЕБЯ ПАРТИЗАНОМ, ПОПАВШИМ В ГЕСТАПО И БУДЕТ ТЕРПЕТЬ ПЫТКИ, НО НЕ ВЫДАСТ ВОЕННУЮ ТАЙНУ, ЭТО ЕГО, АВТОРА ВОПРОСА, ЛИЧНЫЕ ПРОБЛЕМЫ И ШАНСЫ ЕГО ПОЛУЧИТЬ ПОМОЩЬ РЕЗКО УМЕНЬШАЮТСЯ.

Извиняюсь за крик, наболело.


 
Nick Denry ©   (2005-09-14 20:04) [26]

Во-вторых, вызывается ли UnregisterClass в библиотеке при ее освобождении

да на dll_process_detach

Во-первых, зачем библиотеку освобождать ?

ну я загружаю ее динамически, поэтому по хорошему, ее надо освобождать...

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

ПОЧЕМУ ЛЮДИ НИКОГДА НЕ ПИШУТ ДЕТАЛЬНОЕ ОПИСАНИЕ ПРОБЛЕМЫ

(ничего страшного :)
(а может и очень страшно, но я не специально и уж тем более не партизан :)

Я лично предположил, что такая проблема должна быть достаточно известна  
или имеет известное логическое объяснение и

выгрузка библиотеки (после оконного цикла):

While GetMessage(Mesg,0,0,0) do
begin
 TranslateMessage(Mesg);
 DispatchMessage(Mesg);
end;
FreeLibrary(stdCtrlsLib);

end.


дало отсутвие появления исключения.

Если освобождать на WM_DESROY:

то получаю следующее исключение:

---------------------------
Dialog: test.exe - Ошибка приложения
---------------------------
Инструкция по адресу "0x00793db0" обратилась к памяти по адресу "0x00793db0". Память не может быть "read".

"ОК" -- завершение приложения
"Отмена" -- отладка приложения
---------------------------
OK   Отмена  
---------------------------



Страницы: 1 вся ветка

Текущий архив: 2005.11.13;
Скачать: CL | DM;

Наверх




Память: 0.56 MB
Время: 0.038 c
14-1130020587
kaif
2005-10-23 02:36
2005.11.13
Высококачественный перевод на английский


1-1129894645
AMB
2005-10-21 15:37
2005.11.13
Запрет на изменение размера формы


1-1130300515
MikeGipson
2005-10-26 08:21
2005.11.13
Не шевелится поток в ХР


3-1127910446
B@BY
2005-09-28 16:27
2005.11.13
Как создать DBF, используя только DAO ?


14-1130154210
Nic
2005-10-24 15:43
2005.11.13
Разработка форума