发现2个很奇怪的问题。procedure TForm1.FormCreate(Sender: TObject);
var
  Icon: TIcon;
begin
  Icon:= TIcon.Create;
  Icon.LoadFromFile('D:\1.ico'); // 1.ico 尺寸为16x16
  Memo1.Lines.Add( IntToStr( Icon.Width ) ); // 获取的Width为32  Self.Canvas.Draw(0,0,Icon); // 但如果执行过一次Canvas.Draw绘制,就可以正确获取到尺寸。
  Memo1.Lines.Add( IntToStr( Icon.Width ) ); // 正确获取到Width=16
  Icon.Free;
end;但在实际应有中,不可能去执行一次Draw,请问这个Bug有方法解决吗?---------------------------------
第2个问题
我在窗口上有一个弹出菜单,PopupMenu1,我想让它弹出时,向上弹出(类似Windows的开始菜单)。
我现在用的方法是获取菜单中每个Item的高度,然后计算坐标后,再Popup(X,Y);
获取弹出菜单的高度的函数如下,是我在网上找到的。
function GetPopMenuHeight(hWnd: THandle; PopupMenu: TPopupMenu): Integer;
var
  I: Integer;
  lprcRect: TRect;
begin
  Result:= 0;
  for I:= 0 to PopupMenu.Items.Count - 1 do begin
    GetMenuItemRect(hWnd, PopupMenu.Handle, I, lprcRect);
    Inc(Result, lprcRect.Bottom - lprcRect.Top);
  end;
end;但这函数在菜单第一次弹出是,获取到的高度都是0,第二次弹出菜单时,获取到的高度才正确。
调试后,发现菜单第一次弹出是GetMenuItemRect函数获取到的结果是错误的。
能不能向大家请教下,用什么方法才能正确的获取到菜单的高度,或者说,有什么方法能实现向上弹出菜单。

解决方案 »

  1.   

    第一个问题可能是你已经把1.ico读进去了,但是并没有画出来,所以取出来的还是刚创建时的尺寸(默认32)第二个没看出什么问题
      

  2.   

    因为我需要将Icon绘制在另一张Bitmap上,而绘制的坐标X,Y是需要根据Icon的Width,Height来定的,所以需要在绘制前读取Icon的Width,Height.
    难道只有在绘制前,将Icon的Width和Height作为参数传入的方法吗?第二个问题的代码是这样的procedure TForm1.Button1MouseDown(Sender: TObject;
      Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
    var
      lpPoint: TPoint;
      MenuHeight: Integer;
    begin
      if Button = mbLeft then begin
        lpPoint.X:= Button1.Left;
        lpPoint.Y:= Button1.Top;
        windows.ClientToScreen(Button1.Handle, lpPoint); // 转Button1的坐标为屏幕坐标    MenuHeight:= GetPopMenuHeight(Self.Handle, PopupMenu1); // 获取弹出菜单的高度(函数在首帖里)
        PopupMenu1.Popup(lpPoint.X, lpPoint.Y - MenuHeight); // 弹出菜单,第一次弹出时,MenuHeight为0,但第二次就正确了。
      end;
    end;
      

  3.   

    第二个问题:
    重写消息处理函数
       procedure WMContextMenu(var Message: TWMContextMenu); message
    下面是实现。
    procedure TForm1.WMContextMenu(var Message: TWMContextMenu);
    begin
      Message.Pos.y := Message.Pos.y - GetSystemMetrics(SM_CYMENU) * PopupMenu1.Items.Count;
      inherited;
    end;
      

  4.   

    丢了点东西,补上   
     procedure WMContextMenu(var Message: TWMContextMenu); message  WM_CONTEXTMENU;
      

  5.   

    to: jwpl190 
    你的方法也不行呢,GetSystemMetrics(SM_CYMENU)只是获取了系统中一个MenuItem所占像素的高度,是固定的。刚才测了下,如果PopupMenu中的Item有关联图标,或有作为分隔符的Item(就是Item.Caption:= '-'),那获取到的高度就不再正确了。在 WM_CONTEXTMENU 消息中执行GetMenuItemRect函数,第一次仍旧返回错误结果,哎
      

  6.   

    我再改! 看来关键时刻还是WIN32 SDK 基础啊。procedure TFMenu.WMContextMenu(var Message: TWMContextMenu);
    var
      AFlags: Integer;
      x, y: Integer;
    begin
      x := Message.Pos.x;
      y := Message.Pos.y;
      AFlags := TPM_LEFTALIGN or TPM_BOTTOMALIGN; // 这个值你自己可以调,参考MSDN。
      TrackPopupMenu(PopupMenu1.Handle, AFlags, x, y, 0, PopupList.Window, nil);
    end;
      

  7.   

    还好了,呵呵,终于搞定了,谢谢jwpl190,我之前也试过用 TrackPopupMenu但没试成功,^_^,万分感谢。关于Icon.loadfromfile后,在没被绘制前,获取width和height是错误的问题有人能帮忙下吗?
      

  8.   

    看了一下TIcon的源码,发现果然有问题:
    function TIcon.GetHeight: Integer;
    begin
      Result := FImage.FSize.y;//载入文件后第一次使用时,它会返回0,这样得到的结果就不正确了
      if Result = 0 then
        Result := GetSystemMetrics(SM_CYICON)
    end;解决办法,还是用API来取:
    procedure TForm1.Button1Click(Sender: TObject);
    var
      Icon: TIcon;
      IconInf: TIconInfo;
    begin
      Icon := TIcon.Create;
      Icon.LoadFromFile('c:\1.ico '); //   1.ico   尺寸为16x16
      GetIconInfo(Icon.Handle, IconInf); //使用API获取真正的尺寸
      Memo1.Lines.Add(IntToStr(IconInf.xHotspot * 2)); //获取的Width为16
      Self.Canvas.Draw(0, 0, Icon); //   但如果执行过一次Canvas.Draw绘制,就可以正确获取到尺寸。
      Memo1.Lines.Add(IntToStr(Icon.Width)); //   正确获取到Width=16
      Icon.Free;end;
      

  9.   

    谢谢 xzhifei, 呵呵,终于都解决了~~给分,结贴~~
      

  10.   

    刚才忘记释放了,加了两行procedure   TForm1.Button1Click(Sender:   TObject); 
    var 
        Icon:   TIcon; 
        IconInf:   TIconInfo; 
    begin 
        Icon   :=   TIcon.Create; 
        Icon.LoadFromFile( 'c:\1.ico   ');   //       1.ico       尺寸为16x16 
        GetIconInfo(Icon.Handle,   IconInf);   //使用API获取真正的尺寸 
        Memo1.Lines.Add(IntToStr(IconInf.xHotspot   *   2));   //获取的Width为16 
        Self.Canvas.Draw(0,   0,   Icon);   //       但如果执行过一次Canvas.Draw绘制,就可以正确获取到尺寸。 
        Memo1.Lines.Add(IntToStr(Icon.Width));   //       正确获取到Width=16 
        Icon.Free; 
        DeleteObject(IconInf.hbmColor);
        DeleteObject(IconInf.hbmMask);end;