Текущий архив: 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.55 MB
Время: 0.035 c