Конференция "WinAPI" » Как перекрасить меню полностью [D7]
 
  • Nikfel (26.07.09 19:18) [0]
    Подскажите, пожалуйста. Никак не могу разобраться как на чистом WinApi сделать меню с прорисовкой своих пунктов. Меню у меня лежит в файле ресурсов. Загружаю его и делаю стиль OwnerDraw для всех пунктов. Проблема в том как правильно обработать сообщения WM_MeasureItem и WM_DrawItem.

    [code]
    Var mis:PMeasureItemStruct;
    DIS:PDrawItemStruct;
    WM_MeasureItem:begin
    mis:=PMeasureItemStruct(LParam);
    //проблема как тут узнать что это главное меню и надо задать для каждого пункта меню ширину
    //знаю что это делает mis.itemWidth, но как узнать сам пункт меню? Можно ли тут получить    
    //hmenu, зная которое можно пробежаться по всем пунктам меню?
    end;
    WM_DrawItem:begin
    DIS := PDrawItemStruct(LParam);
    //тут проблема в том как получить текст пункта меню, через GetMenuItemInfo получить можно, но
    //если стиль OwnerDraw, то тогда нельзя.
    end;[/code]

    Может быть кто-нибудь писал такое на чистом WinApi на Delphi без VCL? Если можно то хотелось бы посмотреть пример? Я пытался сам написать код, но получилось совсем плохо, много проблем. Заранее спасибо.
  • clickmaker © (27.07.09 11:46) [1]
    > тут проблема в том как получить текст пункта меню, через
    > GetMenuItemInfo получить можно, но
    > //если стиль OwnerDraw, то тогда нельзя.

    itemData
    туда можно положить все, что угодно, см. InsertMenu()
  • Nikfel (27.07.09 12:09) [2]
    Спасибо. Попробую. Значит можно использовать SetMenuItemInfo для записи туда нужного значения?
  • Nikfel (27.07.09 13:37) [3]
    typedef struct tagMYITEM {
    HFONT hfont;
    int cchItemText;
    char szItemText[1];
    }
    MYITEM;



    type
    PMYITEM = ^MYITEM;
    MYITEM = record
     hfont: HFONT;
     cchItemText: integer;
     szItemText:Char;
    end;



    Правильно ли перевел на Delphi c языка C++?

    Как перевести на Delphi вот это:

    MYITEM *pMyItem;

    pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,
    sizeof(MYITEM) + CCH_MAXITEMTEXT);
    pMyItem->cchItemText = mii.cch;
    // Перераспределим память для структуры для
     // минимально требуемого размера.
    pMyItem = (MYITEM *) LocalReAlloc(pMyItem,
    sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);
    pMyItem->hfont = CreateMenuItemFont(uID);
    mii.dwItemData = (DWORD) pMyItem;



    Взято с http://netcode.ru/cpp/?lang=&katID=8&skatID=93&artID=3634
  • clickmaker © (27.07.09 14:03) [4]
    ну да. dwItemData
    только, если размер данных больше размера указателя, то выделять память надо в куче, локальные переменные использовать нельзя
  • Nikfel (28.07.09 14:13) [5]
    program menu_;

    uses
    Windows, Messages, commctrl;

    type
    MYITEM = record
     szItemText:String[30];
     ico:Longint;
    end;
    var intKD:integer;
    myItem_:array[0..17] of MYITEM;//кол-во пунктов меню, не считая separator
    const HeightItem=20;
         HeightItemSeparator=4;
         LeftText=5; //для главного меню смещение текста
         LeftText_=30; //для выпадающего меню смещение текста

    procedure SetMenuOwnerDraw(menu1:HMenu);//изменяем стиль для пунктов меню на mf_OwnerDraw, чтобы можно было рисовать
     type TinfoMenu = record
       info:MENUITEMINFO;
       b:boolean;
     end;
    var i:integer;
    mii:TinfoMenu;
    minf:menuiteminfo;
     function GetMenuIDName(Menu_:HMenu; INDEX:integer):TinfoMenu;//получаем информацию о пункте меню
     var Buffer: array[0..30] of Char;
     begin
       result.info.cbSize        := SizeOf(MENUITEMINFO);
       result.info.fMask         := MIIM_FTYPE or miim_submenu or miim_string;
       result.info.dwTypeData    := buffer;
       result.info.cch           := sizeof(buffer);
     If GetMenuItemInfo(Menu_,INDEX,true,result.info) then begin
       result.b:=true;
     end;
     end;
     function getico_(intI_:integer):HICON;//для установки значка для пункта меню
     begin
     Result:=0;
       case intI_ of
         1:Result:=loadicon(hinstance,'MENUNEW');
         2:Result:=loadicon(hinstance,'MENUOPEN');
         4:Result:=loadicon(hinstance,'MENUSAVE');
         9:Result:=loadicon(hinstance,'MENUEXIT');
         11:Result:=loadicon(hinstance,'MENUCUT');
         12:Result:=loadicon(hinstance,'MENUCOPY');
         13:Result:=loadicon(hinstance,'MENUPASTE');
         14:Result:=loadicon(hinstance,'MENUDELETE');
         16:Result:=loadicon(hinstance,'MENUHELP');
       end;
     end;
    begin
    if menu1<>0 then begin
     for i:=0 to GetMenuItemCount(menu1)-1 do begin//цикл по всем пунктам меню
       mii:=GetMenuIDName(menu1,i);
       if mii.b then begin//если получить информацию о пункте удалось то
         if MII.info.fType=MF_SEPARATOR then begin//если SEPARATOR то так:
           ModifyMenu(menu1,i,mf_OwnerDraw or MF_SEPARATOR or mf_ByPosition,0,nil);
         end else begin
         minf.cbSize:= sizeof(MENUITEMINFO);
         minf.fMask := MIIM_TYPE or MIIM_DATA;
         minf.fType := MFT_OWNERDRAW;
         minf.dwItemData :=intKD;
         myItem_[intKD].szItemText:=MII.info.dwTypeData;
         myItem_[intKD].ico:=getico_(intKD);
         SetMenuItemInfo(menu1,i,true,minf);
         inc(intKD);
         end;
         if MII.info.hSubMenu <>0 then SetMenuOwnerDraw(MII.info.hSubMenu);//если есть выпадающее меню, то снова вызываем процедуру
       end;
     end;
    end;
    end;

    procedure Line(DC_:HDC;x1,y1,x2,y2:integer);
    begin
     MoveToEx(DC_,x1,y1,nil);
     LineTo(DC_,x2,y2);
    end;

    procedure drawmenu(ID,intX,Left_Text:Longint;DC:hdc;Move:boolean;R:TRect);
    const Color1:array[0..19] of integer=
    ($00FAE9D6,$00FAE9D6,$00FAE7CE,$00F8E2C5,$00F5DFBC,
    $00F5DFBB,$00F3DBB8,$00F3DBB8,$00F2D9B7,
    $00F1D8B3,$00F1D8B1,$00EFD3AA,$00EFD3AA,
    $00EED2A6,$00ECD0A1,$00ECD0A1,$00EED2A6,
    $00EED3A7,$00F0D4AB,$00F0D4AB);
    Color2=$00F3AE7E;
    var hMemDC:hDC;
    pen,oldPen:Hpen;
    Brush,oldBrush:HBRUSH;
    hOldBitmap,Bitmap:HBitmap;
    i:integer;
    myfont,oldfont:hfont;
    Height_,WidthItem:integer;
    strText:string;
    begin
     hMemDC:=CreateCompatibleDC(DC);
     if hMemDC<>0 then begin
       WidthItem:=R.Right-R.Left;
       if ID=0 then strText:='' else strText:=myItem_[intX].szItemText;
       if strText='' then Height_:=HeightItemSeparator else Height_:=HeightItem;
       Bitmap:=CreateCompatibleBitmap(dc,WidthItem,Height_);//создаем изображение на котором будем рисовать
       hOldBitmap:=SelectObject(hMemDC, Bitmap);
       SetBkMode(hMemDC, TRANSPARENT);//задаем прозрачный фон для вывода текста
       SetBkMode(DC, TRANSPARENT);
       myfont := CreateFont(HeightItem-4, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, 'Tahoma');
       oldfont:=SelectObject(hMemDC,myfont);
       Brush:=CreateSolidBrush(rgb(240,240,240));
       oldBrush:=SelectObject(hMemDC,Brush);
       PatBlt(hMemDC,0,0,WidthItem,HeightItem,rgb(240,240,240));
       if Move then begin
         for i:=0 to HeightItem-1 do begin
           //рисуем фон для выделенного пункта меню
           Pen:=CreatePen(PS_SOLID, 1, Color1[i]);
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,0,i,WidthItem,i);
           SelectObject(hMemDC,oldpen);
           DeleteObject(pen);
         end;
         //рисуем рамку для выделенного пункта меню
         Pen:=CreatePen(PS_SOLID, 1, Color2);
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,1,0,WidthItem-1,0);
         Line(hMemDC,1,HeightItem-1,WidthItem-1,HeightItem-1);
         Line(hMemDC,0,1,0,HeightItem-1);
         Line(hMemDC,WidthItem-1,1,WidthItem-1,HeightItem-1);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
       end else begin
         if Left_Text=LeftText_ then begin //если главное меню то не рисуем
           Pen:=CreatePen(PS_SOLID, 1, rgb(224,224,224));
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,25,0,25,HeightItem);
           SelectObject(hMemDC,oldpen);
           DeleteObject(Pen);
           Pen:=CreatePen(PS_SOLID, 1, rgb(255,255,255));
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,26,0,26,HeightItem);
           SelectObject(hMemDC,oldpen);
           DeleteObject(Pen);
         end;
       end;
       if strText<>'' then begin
         textout(hMemDC,Left_Text,2,pchar(strText),length(strText));
         //выводим картинку
         if myItem_[intX].ico>0 then DrawIconEx(hMemDC,5,2,myItem_[intX].ico,HeightItem-4,HeightItem-4,0,0,3);
       end else begin
         //если separator то
         Pen:=CreatePen(PS_SOLID, 1, rgb(224,224,224));
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,27,1,WidthItem-1,1);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
         Pen:=CreatePen(PS_SOLID, 1, rgb(255,255,255));
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,27,2,WidthItem-1,2);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
       end;
       BitBlt(DC, r.Left, r.Top, WidthItem, HeightItem, hMemDC, 0, 0, SRCCOPY);
       SelectObject(hMemDC,oldfont);
       SelectObject(hMemDC,oldBrush);
       DeleteObject(myfont);
       DeleteObject(Brush);
       SelectObject(hMemDC, hOldBitmap);
       DeleteObject(Bitmap);
       DeleteDC(hMemDC);
     end;
    end;

  • Nikfel (28.07.09 14:14) [6]
    function GetTextWidth(wnd_:hwnd;strS:string):integer;//получаем ширину текста
    var DC_:HDC;
    myfont,oldfont:HFont;
    siz:Size;
    begin
    DC_:=GetDC(wnd_);
    myfont := CreateFont(HeightItem-4, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, 'Tahoma');
    oldfont:=SelectObject(DC_,myfont);
    GetTextExtentPoint32(DC_,pchar(strS),length(strS),siz);
    result:=siz.cx;
    SelectObject(DC_,oldfont);
    DeleteObject(myfont);
    ReleaseDC(wnd_,DC_);
    end;

    procedure drawMenuWnd(DC:HDC);
    var h:hwnd;
    pen,oldPen:Hpen;
    DC_:hdc;
    t:trect;
    begin
    h:=WindowFromDC(DC);
    DC_:=GetWindowDC(h);
    GetClientRect(h,t);
    Pen:=CreatePen(PS_SOLID, 1, rgb(127,157,185));
    oldPen:=SelectObject(DC_,pen);
    Line(DC_,0,0,t.Right+6,0);
    Line(DC_,0,0,0,t.Bottom+6);
    Line(DC_,t.Right+5,0,t.Right+5,t.Bottom+6);
    Line(DC_,0,t.Bottom+5,t.Right+5,t.Bottom+5);
    SelectObject(DC_,oldpen);
    DeleteObject(Pen);
    ReleaseDC(h,DC_);
    end;

    function DlgProc(hWin, uMsg, wParam, lParam: Integer): Integer; stdcall;
    var t:tpoint;
    DIS:PDRAWITEMSTRUCT;
    mis:PMeasureItemStruct;
    begin
    Result := 0;
     case uMsg of
      WM_INITDIALOG:
       begin
        intKD:=0;
        SendMessage(hWin, WM_SETICON, ICON_BIG, LoadIcon(hInstance, 'MAINICON'));
        SetMenu(hwin,LoadMenu(HInstance, 'MAINMENU'));
        SetMenuOwnerDraw(GetMenu(hwin));
       end;
      WM_MeasureItem:begin
       mis:=PMeasureItemStruct(LParam);
       if mis.CtlType=ODT_MENU then begin
         mis.itemHeight:=HeightItem;
         if mis.itemID=0 then mis.itemHeight:=HeightItemSeparator;
         mis.itemWidth:=GetTextWidth(hwin,myItem_[mis.itemData].szItemText)+20;
         if MIS.itemID>10000 then mis.itemWidth:=GetTextWidth(hwin,myItem_[mis.itemData].szItemText);//если заголовок главного меню
       end;
        end;
      WM_DrawItem:begin
       DIS := PDrawItemStruct(LParam);
       if integer(WindowFromDC(dis.hDC))<>hwin then drawMenuWnd(dis.hDC);//рисуем по всему окну меню
       if DIS.itemState=257 then begin
         //при помещении курсора мыши над элементом меню или при нажатом пункте главного меню
           if dis.hwndItem=getmenu(hwin) then drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,true,dis.rcItem) else
             drawmenu(dis.itemID,dis.itemData,LeftText_,dis.hDC,true,dis.rcItem);
       end else begin
         //при помещении курсора мыши над элементом главного меню и при нажатии кнопки Alt
           if (DIS.itemState=320) or (DIS.itemState=1) then begin
             if dis.hwndItem=getmenu(hwin) then drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,true,dis.rcItem) else
               drawmenu(dis.itemID,dis.itemData,LeftText_,dis.hDC,true,dis.rcItem);
           end else begin
         //когда курсор мыши уходит с пункта меню
             if dis.hwndItem=getmenu(hwin) then drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,false,dis.rcItem) else  //5
               drawmenu(dis.itemID,dis.itemData,LeftText_,dis.hDC,false,dis.rcItem);
           end;
       end;
      end;
      WM_COMMAND:
       begin
        case LoWord(wParam) of
         1001,1002: EndDialog(hWin, 0);
         1003:begin
         //вызов popup меню
         GetCursorPos(t);
         TrackPopupMenuEx(getsubmenu(getmenu(hwin),1),0,t.X,t.Y,hwin,nil);
         end;
       end;
     end;
      WM_DESTROY, WM_CLOSE: begin
      PostQuitMessage(0);
      end;
    end;
    end;

    begin
    {$R WindowsXP.res}
    {$R menu.res}
    InitCommonControls;
    DialogBox(hInstance, 'MAIN_WINDOW', 0, @DlgProc);
    end.

  • Nikfel (28.07.09 14:15) [7]
    Вот решил вот так записать. Где могут быть ошибки? Как лучше записать?  Почему-то ширину получаю не правильно, если submenu. Как исправить?
  • Nikfel (29.07.09 16:12) [8]
    Вот исправленный вариант меню:
    program menu_;

    uses
    Windows, Messages, commctrl;

    type
    MYITEM = record
     szItemText:String[30];
     ico:Longint;
     b:boolean;
    end;
    var intKD:integer;
    myItem_:array[0..17] of MYITEM;//кол-во пунктов меню, не считая separator
    const HeightItem=20;
         HeightItemSeparator=4;
         LeftText=5; //для главного меню смещение текста
         LeftText_=30; //для выпадающего меню смещение текста

    procedure SetMenuOwnerDraw(menu1:HMenu; wnd:hwnd);//изменяем стиль для пунктов меню на mf_OwnerDraw, чтобы можно было рисовать
     type TinfoMenu = record
       info:MENUITEMINFO;
       b:boolean;
     end;
    var i:integer;
    mii:TinfoMenu;
    minf:menuiteminfo;
     function GetMenuIDName(Menu_:HMenu; INDEX:integer):TinfoMenu;//получаем информацию о пункте меню
     var Buffer: array[0..30] of Char;
     begin
       result.info.cbSize        := SizeOf(MENUITEMINFO);
       result.info.fMask         := MIIM_FTYPE or miim_submenu or miim_string;
       result.info.dwTypeData    := buffer;
       result.info.cch           := sizeof(buffer);
     If GetMenuItemInfo(Menu_,INDEX,true,result.info) then begin
       result.b:=true;
     end;
     end;
     function getico_(intI_:integer):HICON;//для установки значка для пункта меню
     begin
     Result:=0;
       case intI_ of
         1:Result:=loadicon(hinstance,'MENUNEW');
         2:Result:=loadicon(hinstance,'MENUOPEN');
         4:Result:=loadicon(hinstance,'MENUSAVE');
         9:Result:=loadicon(hinstance,'MENUEXIT');
         11:Result:=loadicon(hinstance,'MENUCUT');
         12:Result:=loadicon(hinstance,'MENUCOPY');
         13:Result:=loadicon(hinstance,'MENUPASTE');
         14:Result:=loadicon(hinstance,'MENUDELETE');
         16:Result:=loadicon(hinstance,'MENUHELP');
       end;
     end;
    begin
    if menu1<>0 then begin
     for i:=0 to GetMenuItemCount(menu1)-1 do begin//цикл по всем пунктам меню
       mii:=GetMenuIDName(menu1,i);
       if mii.b then begin//если получить информацию о пункте удалось то
         if MII.info.fType=MF_SEPARATOR then begin//если SEPARATOR то так:
           ModifyMenu(menu1,i,mf_OwnerDraw or MF_SEPARATOR or mf_ByPosition,0,nil);
         end else begin
         minf.cbSize:= sizeof(MENUITEMINFO);
         minf.fMask := MIIM_TYPE or MIIM_DATA;
         minf.fType := MFT_OWNERDRAW;
         minf.dwItemData :=intKD;
         myItem_[intKD].szItemText:=MII.info.dwTypeData;
         myItem_[intKD].ico:=getico_(intKD);
         if getmenu(wnd)=menu1 then myItem_[intKD].b:=true else myItem_[intKD].b:=false;
         SetMenuItemInfo(menu1,i,true,minf);
         inc(intKD);
         end;
         if MII.info.hSubMenu <>0 then SetMenuOwnerDraw(MII.info.hSubMenu,wnd);//если есть выпадающее меню, то снова вызываем процедуру
       end;
     end;
    end;
    end;

    procedure Line(DC_:HDC;x1,y1,x2,y2:integer);
    begin
     MoveToEx(DC_,x1,y1,nil);
     LineTo(DC_,x2,y2);
    end;

    procedure drawmenu(ID,intX,Left_Text:Longint;DC:hdc;Move:boolean;R:TRect);
    const Color1:array[0..19] of integer=
    ($00FAE9D6,$00FAE9D6,$00FAE7CE,$00F8E2C5,$00F5DFBC,
    $00F5DFBB,$00F3DBB8,$00F3DBB8,$00F2D9B7,
    $00F1D8B3,$00F1D8B1,$00EFD3AA,$00EFD3AA,
    $00EED2A6,$00ECD0A1,$00ECD0A1,$00EED2A6,
    $00EED3A7,$00F0D4AB,$00F0D4AB);
    Color2=$00F3AE7E;
    var hMemDC:hDC;
    pen,oldPen:Hpen;
    Brush,oldBrush:HBRUSH;
    hOldBitmap,Bitmap:HBitmap;
    i:integer;
    myfont,oldfont:hfont;
    Height_,WidthItem:integer;
    strText:string;
    begin
     hMemDC:=CreateCompatibleDC(DC);
     if hMemDC<>0 then begin
       WidthItem:=R.Right-R.Left;
       if ID=0 then strText:='' else strText:=myItem_[intX].szItemText;
       if strText='' then Height_:=HeightItemSeparator else Height_:=HeightItem;
       Bitmap:=CreateCompatibleBitmap(dc,WidthItem,Height_);//создаем изображение на котором будем рисовать
       hOldBitmap:=SelectObject(hMemDC, Bitmap);
       SetBkMode(hMemDC, TRANSPARENT);//задаем прозрачный фон для вывода текста
       SetBkMode(DC, TRANSPARENT);
       myfont := CreateFont(HeightItem-4, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, 'Tahoma');
       oldfont:=SelectObject(hMemDC,myfont);
       Brush:=CreateSolidBrush(rgb(240,240,240));
       oldBrush:=SelectObject(hMemDC,Brush);
       PatBlt(hMemDC,0,0,WidthItem,HeightItem,rgb(240,240,240));
       if Move then begin
         for i:=0 to HeightItem-1 do begin
           //рисуем фон для выделенного пункта меню
           Pen:=CreatePen(PS_SOLID, 1, Color1[i]);
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,0,i,WidthItem,i);
           SelectObject(hMemDC,oldpen);
           DeleteObject(pen);
         end;
         //рисуем рамку для выделенного пункта меню
         Pen:=CreatePen(PS_SOLID, 1, Color2);
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,1,0,WidthItem-1,0);
         Line(hMemDC,1,HeightItem-1,WidthItem-1,HeightItem-1);
         Line(hMemDC,0,1,0,HeightItem-1);
         Line(hMemDC,WidthItem-1,1,WidthItem-1,HeightItem-1);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
       end else begin
         if Left_Text=LeftText_ then begin //если главное меню то не рисуем
           Pen:=CreatePen(PS_SOLID, 1, rgb(224,224,224));
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,25,0,25,HeightItem);
           SelectObject(hMemDC,oldpen);
           DeleteObject(Pen);
           Pen:=CreatePen(PS_SOLID, 1, rgb(255,255,255));
           oldPen:=SelectObject(hMemDC,pen);
           Line(hMemDC,26,0,26,HeightItem);
           SelectObject(hMemDC,oldpen);
           DeleteObject(Pen);
         end;
       end;
       if strText<>'' then begin
         textout(hMemDC,Left_Text,2,pchar(strText),length(strText));
         //выводим картинку
         if myItem_[intX].ico>0 then DrawIconEx(hMemDC,5,2,myItem_[intX].ico,16,16,0,0,3);
       end else begin
         //если separator то
         Pen:=CreatePen(PS_SOLID, 1, rgb(224,224,224));
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,27,1,WidthItem-1,1);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
         Pen:=CreatePen(PS_SOLID, 1, rgb(255,255,255));
         oldPen:=SelectObject(hMemDC,pen);
         Line(hMemDC,27,2,WidthItem-1,2);
         SelectObject(hMemDC,oldpen);
         DeleteObject(Pen);
       end;
       BitBlt(DC, r.Left, r.Top, WidthItem, HeightItem, hMemDC, 0, 0, SRCCOPY);
       SelectObject(hMemDC,oldfont);
       SelectObject(hMemDC,oldBrush);
       DeleteObject(myfont);
       DeleteObject(Brush);
       SelectObject(hMemDC, hOldBitmap);
       DeleteObject(Bitmap);
       DeleteDC(hMemDC);
     end;
    end;

  • Nikfel (29.07.09 16:13) [9]
    function GetTextWidth(wnd_:hwnd;strS:string):integer;//получаем ширину текста
    var DC_:HDC;
    myfont,oldfont:HFont;
    siz:Size;
    begin
    DC_:=GetDC(wnd_);
    myfont := CreateFont(HeightItem-4, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, 'Tahoma');
    oldfont:=SelectObject(DC_,myfont);
    GetTextExtentPoint32(DC_,pchar(strS),length(strS),siz);
    result:=siz.cx;
    SelectObject(DC_,oldfont);
    DeleteObject(myfont);
    ReleaseDC(wnd_,DC_);
    end;

    procedure drawMenuWnd(DC:HDC);
    var h:hwnd;
    pen,oldPen:Hpen;
    DC_:hdc;
    t:trect;
    begin
    h:=WindowFromDC(DC);
    DC_:=GetWindowDC(h);
    GetClientRect(h,t);
    Pen:=CreatePen(PS_SOLID, 1, rgb(127,157,185));
    oldPen:=SelectObject(DC_,pen);
    Line(DC_,0,0,t.Right+6,0);
    Line(DC_,0,0,0,t.Bottom+6);
    Line(DC_,t.Right+5,0,t.Right+5,t.Bottom+6);
    Line(DC_,0,t.Bottom+5,t.Right+5,t.Bottom+5);
    SelectObject(DC_,oldpen);
    DeleteObject(Pen);
    ReleaseDC(h,DC_);
    end;

    function DlgProc(hWin, uMsg, wParam, lParam: Integer): Integer; stdcall;
    var t:tpoint;
    DIS:PDRAWITEMSTRUCT;
    mis:PMeasureItemStruct;
    const LeftSub=15;
         LeftMenu=20;
    begin
    Result := 0;
     case uMsg of
      WM_INITDIALOG:
       begin
        intKD:=0;
        SendMessage(hWin, WM_SETICON, ICON_BIG, LoadIcon(hInstance, 'MAINICON'));
        SetMenu(hwin,LoadMenu(HInstance, 'MAINMENU'));
        SetMenuOwnerDraw(GetMenu(hwin),hwin);
       end;
      WM_MeasureItem:begin
       mis:=PMeasureItemStruct(LParam);
       if mis.CtlType=ODT_MENU then begin
         if MIS.itemID>10000 then begin
           if myItem_[mis.itemData].b then mis.itemWidth:=GetTextWidth(hwin,myItem_[mis.itemData].szItemText) else //если заголовок главного меню
             mis.itemWidth:=GetTextWidth(hwin,myItem_[mis.itemData].szItemText)+LeftMenu+LeftSub;//если submenu
           mis.itemHeight:=HeightItem;
         end else begin
           mis.itemWidth:=GetTextWidth(hwin,myItem_[mis.itemData].szItemText)+LeftMenu;
           if mis.itemID=0 then mis.itemHeight:=HeightItemSeparator else mis.itemHeight:=HeightItem;
         end;
       end;
        end;
      WM_DrawItem:begin
       DIS := PDrawItemStruct(LParam);
       if integer(WindowFromDC(dis.hDC))<>hwin then drawMenuWnd(dis.hDC);//рисуем по всему окну меню
       if DIS.itemState=257 then begin
         //при помещении курсора мыши над элементом меню или при нажатом пункте главного меню
           if dis.hwndItem=getmenu(hwin) then drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,true,dis.rcItem) else
             drawmenu(dis.itemID,dis.itemData,LeftText_,dis.hDC,true,dis.rcItem);
       end else begin
         //при помещении курсора мыши над элементом главного меню и при нажатии кнопки Alt
           if (DIS.itemState=320) or (DIS.itemState=1) then begin
             drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,true,dis.rcItem);
           end else begin
         //когда курсор мыши уходит с пункта меню
             if dis.hwndItem=getmenu(hwin) then drawmenu(dis.itemID,dis.itemData,LeftText,dis.hDC,false,dis.rcItem) else  //5
               drawmenu(dis.itemID,dis.itemData,LeftText_,dis.hDC,false,dis.rcItem);
           end;
       end;
      end;
      WM_COMMAND:
       begin
        case LoWord(wParam) of
         1001,1002: EndDialog(hWin, 0);
         1003:begin
         //вызов popup меню
         GetCursorPos(t);
         TrackPopupMenuEx(getsubmenu(getmenu(hwin),1),0,t.X,t.Y,hwin,nil);
         end;
       end;
     end;
      WM_DESTROY, WM_CLOSE: begin
      PostQuitMessage(0);
      end;
    end;
    end;

    begin
    {$R WindowsXP.res}
    {$R menu.res}
    InitCommonControls;
    DialogBox(hInstance, 'MAIN_WINDOW', 0, @DlgProc);
    end.

  • Nikfel (29.07.09 16:15) [10]
    Вот исправленный вариант меню, но только странно то что если задаешь
    myItem_:array[0..0] of MYITEM;


    то это не вызывает ошибку. Возможно могут быть утечки памяти или из-за чего такое происходит не пойму, т.к. у меня может быть такое myItem_[25].
  • app © (29.07.09 16:28) [11]
    > Nikfel  (29.07.2009 16:15:10)  [10]

    Хватит портянки постить.
  • Nikfel (29.07.09 16:33) [12]
    Так я больше и не собирался ничего сюда писать, но только осталось не понятно только одно: почему
    myItem_:array[0..0] of MYITEM;

    не вызывает ошибку?
  • clickmaker © (29.07.09 18:22) [13]
    > почему myItem_:array[0..0] of MYITEM; не вызывает ошибку?

    везет. При другой фазе Луны может не повезти
 
Конференция "WinAPI" » Как перекрасить меню полностью [D7]
Есть новые Нет новых   [134434   +27][b:0][p:0.012]