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

Вниз

OwnerDraw PopupMenu и его рамка   Найти похожие ветки 

 
DVM ©   (2004-03-10 18:48) [0]

Увидел ниже вопрос про меню и решил задать в чем то похожий другой.
Возможно ли как-то перекрасить рамку Popup Menu в пределах одного приложения. Дело в том что сами Item-ы меню у меня отрисовываются через OwnerDraw, а вокруг них остается серый прямоугольник. Во время отрисовки Item - а, добраться до рамки не получается, т.к. вероятно область отсечения для окна меню установлена так, что рамка меню туда не попадает.


 
Игорь Шевченко ©   (2004-03-10 23:16) [1]

DVM ©   (10.03.04 18:48)

Рамка представляет из себя Window Frame для окна Popup-меню, и относится к неклиентской части того самого окна с классом #32768,
которое отображается для каждого Popup-меню.

Сдается мне, что лучше имитировать Popup-меню, например, ListBox"ом и уже его рисовать, как угодно.
Это, кстати, не так трудно. Впрочем, если вдруг увидишь нарисованное без рамки Popup- или MainMenu, определи класс окна, которое отображается в момент Popup"а - самому интересно, можно ли стандартными средствами отрисовать неклиентскую часть окна меню.


 
Serge ©   (2004-03-11 09:23) [2]

> Игорь Шевченко © "...можно ли стандартными средствами отрисовать неклиентскую часть окна меню"
Вроде как можно - реализованно в компоненте XPMenu при свойстве FlatMenu = true. Если он вам Нужен (в случае если Вы его не видели) могу его переслать, скажите только куда - на почту либо в "Кладовку" (хотя по моему его я оттуда и скачивал года три назад)


 
Игорь Шевченко ©   (2004-03-11 09:57) [3]

Serge ©   (11.03.04 09:23)

Кладовка за это время не один раз реорганизовывалась (иногда спонтанно), поэтому я не надеюсь там найти.
Если не затруднит, то на whitefranz@hotmail.com


 
serge ©   (2004-03-11 10:05) [4]

Почему-же, не затруднит, уже скинул!
Надеюсь поможет.

P.S. А кладовка по моему и сейчас спонтанно реорганизовывается :)


 
Игорь Шевченко ©   (2004-03-11 10:34) [5]

Как получить доступ к окну OwnerDraw меню:

В событии DrawItem надо использовать вызов функции
WindowFromDC, в качестве DC выступает ACanvas.Handle, передаваемый в обработчик события OnDrawItem.
А потом с окном можно делать все необходимые действия.

Как сделано в XPMenu:
 if not (csDesigning in ComponentState) then
 begin
   if (FFlatMenu) and (not FTopMenu) then
   begin
     hDcM := ACanvas.Handle;
     hWndM := WindowFromDC(hDcM);
     if hWndM <> FForm.Handle then
     begin
       DrawWindowBorder(hWndM, FMenu.IsRightToLeft);
     end;
   end;
 end;
...
procedure TXPMenu.DrawWindowBorder(hWnd: HWND; IsRightToLeft: boolean);
var
 WRect, CRect: TRect;
 dCanvas: TCanvas;
begin

 if hWnd <= 0 then
 begin
  exit;
 end;
 dCanvas := nil;
 try
 dCanvas := TCanvas.Create;
 dCanvas.Handle := GetDc(0);

 GetClientRect(hWnd, CRect);
 GetWindowRect(hWnd, WRect);

 ExcludeClipRect(dCanvas.Handle, CRect.Left, CRect.Top, CRect.Right,
                 CRect.Bottom);

 dCanvas.Brush.Style := bsClear;

 Dec(WRect.Right, 2);
 Dec(WRect.Bottom, 2);

 dCanvas.Pen.Color := FMenuBorderColor;
 dCanvas.Rectangle(WRect.Left, WRect.Top, WRect.Right, WRect.Bottom);

 if IsRightToLeft then
 begin
   dCanvas.Pen.Color := FFColor;
   dCanvas.Rectangle(WRect.Left + 1, WRect.Top + 1, WRect.Right - 2,
                     WRect.Top + 3);

   dCanvas.MoveTo(WRect.Left + 2, WRect.Top + 2);
   dCanvas.LineTo(WRect.Left + 2, WRect.Bottom - 2);

   dCanvas.Pen.Color := FFIconBackColor;
   dCanvas.MoveTo(WRect.Right - 2, WRect.Top + 2);
   dCanvas.LineTo(WRect.Right - 2, WRect.Bottom - 2);

   dCanvas.MoveTo(WRect.Right - 2, WRect.Top + 2);
   dCanvas.LineTo(WRect.Right - 1 - FIconWidth, WRect.Top + 2);
 end
 else
 begin
   if not FGradient then
   begin
     dCanvas.Pen.Color := FFColor;
     dCanvas.Rectangle(WRect.Left + 1, WRect.Top + 1, WRect.Right - 2,
                       WRect.Top + 3);

     dCanvas.Pen.Color := FFIconBackColor;
     dCanvas.MoveTo(WRect.Left + 1, WRect.Top + 2);
     dCanvas.LineTo(WRect.Left + 2 + FIconWidth, WRect.Top + 2);
   end;

   dCanvas.Pen.Color := FFIconBackColor;
   dCanvas.MoveTo(WRect.Left + 1, WRect.Top + 1);
   dCanvas.LineTo(WRect.Left + 1, WRect.Bottom - 2);

 end;

 Inc(WRect.Right, 2);
 Inc(WRect.Bottom, 2);

 dCanvas.Pen.Color := FMenuShadowColor;
 dCanvas.Rectangle(WRect.Left +2, WRect.Bottom, WRect.Right, WRect.Bottom - 2);
 dCanvas.Rectangle(WRect.Right - 2, WRect.Bottom, WRect.Right, WRect.Top + 2);

 dCanvas.Pen.Color := FFIconBackColor;
 dCanvas.Rectangle(WRect.Left, WRect.Bottom - 2, WRect.Left + 2, WRect.Bottom);
 dCanvas.Rectangle(WRect.Right - 2, WRect.Top, WRect.Right, WRect.Top + 2);
 finally
 IntersectClipRect(dCanvas.Handle, WRect.Left, WRect.Top, WRect.Right, WRect.Bottom);
 dCanvas.Free;
 end;

end;



 
serge ©   (2004-03-11 10:43) [6]

Да, кстати - забыл сказать.
По исходникам - этого-же компонента делал то-же самое (рамку), так вот что у меня получалось - в Windows 2000 все тип-топ (рамочка и все такое), в XP, 2003 - даже при отключенных темах - ничего, меню отрисовывалось стандартным :(


 
DVM ©   (2004-03-11 18:31) [7]


> Игорь Шевченко ©   (11.03.04 10:34) [5]

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


 
DVM ©   (2004-03-11 22:57) [8]

Попробовал, не то чтобы не получилось. Получилось, но не очень. Рамка меню рисуется только если по нему провести мышкой. Если меню просто показать TrackPopupMenu, то оно без рамки все равно. Тестировалось в WinXP. Вот тестовый код. Есть у кого какие соображения КАК ВСЕ ТАКИ ОТРИСОВАТЬ РАМКУ У МЕНЮ???
XP меню пробовал, вроде там тоже самое, но там работает.

program Test;

uses
 Windows, Messages;

const
 IDB_START = 10;
 szMainWndClass: PChar = "MenuWndClass";
 szMainWndName:  PChar = "MenuDemo";
 szButtonName: PChar = "Click";
 
var
 wc: TWndClassEx;
 hMainWnd: HWND;
 Mesg: TMsg;
 hBtn: HWND;
 hMainMenu: HMENU;

type

PMenuItemData = ^TMenuItemData;
TMenuItemData = packed record
  text: string;
end;

//------------------------------------------------------------------------------

function OnMeasureItem(wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
begin
 PMEASUREITEMSTRUCT (lParam).itemWidth := 200;
 PMEASUREITEMSTRUCT (lParam).itemHeight:=18;
 result:=1;
end;

//------------------------------------------------------------------------------

procedure DrawWindowBorder(Wnd: HWND; IsRightToLeft: boolean);
var
WRect, CRect: TRect;
dc: HDC;
br: HBRUSH;
begin
GetClientRect(Wnd, CRect);
GetWindowRect(Wnd, WRect);
DC := GetDc(0);
BR := CreateSolidBrush(rgb(255,0,0));
ExcludeClipRect(DC, wRect.Left+3, wRect.Top+3, wRect.Right-3, wRect.Bottom-3);
fillrect(dc, wRect, br);
IntersectClipRect(dc, WRect.Left, WRect.Top, WRect.Right, WRect.Bottom);
ReleaseDc(0, dc);
DeleteObject(br);
end;

//------------------------------------------------------------------------------

function OnDrawItem(wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
var
 DC: HDC;
 rc: TRect;
 prevtx,prevbk:colorref;
 lf:logfont;
 buffnt:hfont;
 prevfn:hfont;
 br: HBRUSH;
 txt:string;
 hWndM: HWND;
begin
if (PDRAWITEMSTRUCT (LParam).CtlType = ODT_MENU) then
  begin
     DC := PDRAWITEMSTRUCT(LParam).hDC;
     rc := PDRAWITEMSTRUCT (LParam).rcItem;
     if (PDRAWITEMSTRUCT (LParam).itemState and ODS_SELECTED) = ODS_SELECTED then
       begin
         prevtx:=settextcolor (dc,GetSysColor(COLOR_HIGHLIGHTTEXT));
         prevbk:=setbkcolor (dc,GetSysColor(COLOR_HIGHLIGHT));
         Br:=CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
       end
     else
       begin
         prevtx:=settextcolor (dc,GetSysColor(COLOR_MENUTEXT));
         prevbk:=setbkcolor (dc,(rgb(110,255,60)));
         Br:=CreateSolidBrush((rgb(110,255,60)));
       end;
     FillRect(DC, rc, br);
     DeleteObject(br);

     zeromemory (@lf,sizeof (lf));
     lf.lfHeight:=12;
     lf.lfFaceName:="MS Sans Serif"+#0;
     lf.lfCharSet:=DEFAULT_CHARSET;
     lf.lfWeight:=FW_NORMAL;

     buffnt:=createfontindirect (lf);
     prevfn:=selectobject (dc,buffnt);

     txt:=PMenuItemData(PDRAWITEMSTRUCT (LParam).itemData).text;
     drawtextex(dc,pchar (txt),length (txt),rc,DT_END_ELLIPSIS or DT_LEFT or DT_MODIFYSTRING or DT_NOPREFIX or DT_VCENTER,NIL);

     settextcolor (dc,prevtx);
     selectobject (dc,prevfn);
     setbkcolor (dc,prevbk);
     deleteobject (buffnt);

     hWndM := WindowFromDC(PDRAWITEMSTRUCT (LParam).hDC);
     DrawWindowBorder(hWndM, true);
  end;
 Result:=1;
end;

//------------------------------------------------------------------------------

function OnCommand(wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT;
var
 ctrlID: Word;
 rc: TRect;
begin
 ctrlID := loWord(wParam);
 case ctrlID of
   IDB_START:
     begin
       GetWindowRect(wnd, rc);
       TrackPopupMenu(hMainMenu, TPM_VERNEGANIMATION, rc.Right, rc.Top, 0, Wnd, nil);
     end
 end;
 result := 0;
end;

//------------------------------------------------------------------------------

procedure ClearMenuItemsData;
var
 i: integer;
 mii: TMenuItemInfo;
begin
 for i := 0 to 10 do
   begin
     MII.cbSize := SizeOf(TMenuItemInfo);
     MII.fMask := MIIM_DATA;
     if (GetMenuItemInfo(hMainMenu, i, false, mii)<>false) then
       begin
         Dispose(PMenuItemData(mii.dwItemData));
       end;
   end;
end;

//------------------------------------------------------------------------------

function WindowProc(wnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;

begin
 case msg of
   WM_MEASUREITEM: Result := OnMeasureItem(wnd, Msg, wParam, lParam);
   WM_DRAWITEM: Result := OnDrawItem(wnd, Msg, wParam, lParam);
   WM_COMMAND: Result := OnCommand(wnd, Msg, wParam, lParam);
   WM_DESTROY:
     begin
       ClearMenuItemsData;
       DestroyMenu(hMainMenu);
       PostQuitMessage(0);
       Result := 0;
       exit;
     end;
 else
   Result := DefWindowProc(Wnd, Msg, wParam, lParam);
end;
end;

//------------------------------------------------------------------------------

var
 xPos, yPos, nWidth, nHeight: integer;
 mii: TMenuItemInfo;
 i: integer;


 
DVM ©   (2004-03-11 22:57) [9]

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 := 0;
 wc.hCursor := 0;
 wc.hbrBackground := COLOR_BTNFACE+1;
 wc.lpszMenuName := nil;
 wc.lpszClassName := szMainWndClass;

 if RegisterClassEx(wc) = 0 then
   begin
     MessageBox(0, "Error Register Window Class!", "Error!", 0);
     exit;
   end;

 xPos:=0; yPos:=0; nWidth:=200; nHeight:=100;

 hMainWnd:=CreateWindowEx(0,
                         szMainWndClass,
                         szMainWndName,
                         WS_OVERLAPPEDWINDOW,
                         xPos,
                         yPos,
                         nWidth,
                         nHeight,
                         0,
                         0,
                         hInstance,
                         nil);

 if hMainWnd = 0 then
   begin
     MessageBox(0, "Error Create Window!", "Error!", 0);
     UnregisterClass(szMainWndClass, hInstance);
     exit;
   end;

 hBtn := CreateWindowEx(0,
                       "button",
                       szButtonName,
                       BS_PUSHBUTTON or
                       WS_CHILD or
                       WS_VISIBLE,
                       10,
                       10,
                       75,
                       25,
                       hMainWnd,
                       IDB_START,
                       hInstance,
                       nil);

               
 if hBtn = 0 then
   begin
     MessageBox(0, "Button not Created!", "Error!", 0);
     DestroyWindow(hMainWnd);
     UnregisterClass(szMainWndClass, hInstance);
     exit;
   end;

 ShowWindow(hMainWnd, SW_SHOW);

 hMainMenu:=CreatePopupMenu;
 if hMainMenu<>0 then
   begin
     for i:=0 to 10 do
       begin
         ZeroMemory(@mii, SizeOf(mii));
         New(PMenuItemData(mii.dwItemData));
         mii.cbSize:=SizeOf(TMenuItemInfo);
         mii.fType:=MFT_OWNERDRAW;
         mii.fMask:=MIIM_TYPE or MIIM_DATA  or MIIM_ID;
         mii.wID:=i;
         PMenuItemData(mii.dwItemData).text:="Item";
         InsertMenuItem (hMainMenu, 0, true, mii);
       end;
   end;

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


 
Игорь Шевченко ©   (2004-03-11 23:27) [10]

Я дико извиняюсь, а то же самое на VCL не проще будет отлаживать ? Уж больно код длинный :)


 
Gero ©   (2004-03-12 07:42) [11]

> Сдается мне, что лучше имитировать Popup-меню, например,
> ListBox"ом и уже его рисовать, как угодно.
> Это, кстати, не так трудно

Интересная идея.
Только как насчет исчезновения меню?
Оно ведь должно исчезать когда:
- Вызывается другое или это же меню
- Меню теряет фокус
- Приложение теряет фокус
И еще в нескольких случаях. Как это все можно отслеживать?
Чтобы это меню работало четко, как и стандартное.


 
Игорь Шевченко ©   (2004-03-12 10:12) [12]

Gero ©   (12.03.04 07:42)


> Оно ведь должно исчезать когда:
> - Вызывается другое или это же меню
> - Меню теряет фокус
> - Приложение теряет фокус


На каждое из этих событий окну-владельцу меню посылается сообщение. Я встречал в свое время в сети имитаторы меню, возни много, но способ имеет право на существование. Кстати, имитаторы меню реализованы в DevExpress (TDxBarSubMenuControl), в MSDN и в MSOffice (MSOCommandBarPopup)


 
Gero ©   (2004-03-12 17:30) [13]

> На каждое из этих событий окну-владельцу меню посылается сообщение

А какое именно сообщение, Вы не в курсе?


 
Игорь Шевченко ©   (2004-03-12 23:55) [14]

Gero ©   (12.03.04 17:30)

в первую очередь WM_KILLFOCUS

Также можно посмотреть реализацию класса TPopupListBox в VCL (в DBCtrls.pas, если не ошибаюсь, он себя ведет примерно так же, как меню)


 
Gero ©   (2004-03-13 08:56) [15]

> Игорь Шевченко ©   (12.03.04 23:55) [14]

Спасибо за инфу.


 
Nick Denry ©   (2004-03-13 15:10) [16]

2 > Игорь Шевченко ©  
Наверно, я плохо молился. Или вопрос стал слишком популярен? :))))

Вопрос в тему : можно - ли перерисовывать сепаратор (MENUITEM SEPARATOR) своими силами (т.е. получить линию, которой рисуются все разделители). Это нужно, чтобы нарисовать битмап с надписью с левой стороны меню, как в Пуске....

С уважением, NickD.


 
Gero ©   (2004-03-13 19:04) [17]

Nick Denry ©   (13.03.04 15:10) [16]
> можно - ли перерисовывать сепаратор (MENUITEM SEPARATOR) своими силами

Конечно можно, через OnDrawItem.


 
Nick Denry ©   (2004-03-14 12:52) [18]

2> Gero (c)

Конечно можно, через OnDrawItem.

Вопервых без VCL - это WM_DRAWITEM насколько я понимаю, во - вторых  у сепаратора не может быть стиля MF_OWNERDRAW (MFT_OWNERDRAW). Я думаю, что можно как-то нарисовать (получить, если это битмап) линию, которая рисуется в сепараторе , или нет? А сам сепаратор перерисовывать вроде бесполезно...

С уважением, NIckD.



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

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

Наверх




Память: 0.54 MB
Время: 0.028 c
6-1079111390
LanLan
2004-03-12 20:09
2004.04.11
Обработка исключительных ситуаций


1-1080233401
Kor@l
2004-03-25 19:50
2004.04.11
PChar


11-1059916530
Кладов
2003-08-03 17:15
2004.04.11
FakeVariants


14-1082402837
Gomosapin
2004-04-19 23:27
2004.04.11
Помогите решить простую задачу. Pascal


1-1079973860
sagsoft
2004-03-22 19:44
2004.04.11
Шифрование текста