이 답변이 너무 많은 호언 장담으로 나오지 않았 으면 좋겠지 만, 이것은 엠바 카데로가 오랫동안 오판의 역사를 가지고있는 분야입니다. 나는이 분야에서 많은 QC 보고서를 제출 했으므로 나는 아마도 약간 씁쓸 할 것이다. 즉, 델파이의 최신 릴리스는 허용 가능한 방식으로 메뉴를 구현하는 것 같습니다. 최근 XE6 메뉴를 스핀했을 때 XE6 메뉴를 넘길 수 없었습니다. 그러나 따라 잡기까지 오랜 시간이 걸렸습니다.
귀하의 델파이 사전 비스타. Vista는 Windows 메뉴를위한 훌륭한 창고였습니다. 테마 API는 XP에 도입되었지만 메뉴에는 아무런 영향을 미치지 않았습니다. Vista에서 변경되었습니다. 델파이 7은 그 전이었고 XP를 염두에두고 작성되었습니다.
XP에서는 글리프가있는 그리기 메뉴가 쉽지 않았습니다. MENUITEMINFO
구조체에는 비트 맵 필드 hbmpItem
이 있습니다. 그러나 XP에서는 제한된 사용이 가능합니다. 시스템에 그려진 XP 메뉴는 메뉴에 깨끗한 알파 비트 맵을 그리지 않습니다. 이러한 메뉴에는 소유자 그리기가 필요합니다. Delphi 7 코드에서 메뉴에 글리프가 있으면 소유자가 그려집니다. 그리고 XP API를 사용하여 그린 소유자.
두 가지 스크린 샷의 차이점을 설명합니다. 테마 스크린 샷은 글리프가없는 메뉴입니다. Delphi 7 메뉴 코드는 시스템에 메뉴 그리기를 요청합니다. 그리고 테마 메뉴를 그립니다. comctl32 매니페스트 유무에 관계없이이것이 Vista의 표준 메뉴입니다.
그리고 글리프를 추가 할 때 XP에 대해서만 알고있는 VCL 코드는 소유자가 메뉴를 그리기로 결정합니다. XP 기능을 사용합니다. 결국 Vista 테마 메뉴 API는 사용할 수 없습니다. 이 코드는 그 코드보다 우선합니다.
최신 버전의 Delphi는 점차 Vista 테마 메뉴에 대한 지원을 추가했습니다. Menus
단위의 원래 구현은 모든 정직에서 불쌍했습니다. Embarcadero 디자이너는 테마 API를 사용하여 메뉴를 그리기로 결정했습니다. 모든 의도와 목적에 대해 문서화되지 않은 API. 아마도이 API에 대한 최상의 정보 소스는 Delphi 소스 코드 (!)와 Wine 소스 코드입니다. MSDN에서 도움을 청합니다. 그래서, 여기 엔 엠바 카데로에 대한 동정이있었습니다. 가난한 엔지니어가 이것을 해결해야만했기 때문입니다. 그리고 5 가지 버전의 소프트웨어를 사용하여 버그를 제거하십시오.
그러나 Embarcadero도 강경 한 말을 할 자격이 있습니다. Vista 용 및 글리프가 포함 된 테마 메뉴를 그릴 수 있습니다. 비밀은 hbmpItem
필드입니다. XP에서는 제한적으로 사용되었지만 Vista에서는 자체적으로 제공됩니다. 이 문서는 어디에서나 찾을 수 없습니다. 셸 공개 블로그에 게시 된 MS 직원이 게시 한 블로그 자료 인 문서의 유일한 좋은 출처는 어떤 이유로 인터넷에서 제거되었지만 (archive.org에 캡처 됨) 그러나 세부 사항은 충분히 간단합니다. PARGB32 비트 맵을 hbmpItem
에 넣고 시스템에서 메뉴를 그려 봅니다. 그리고 모든 것이 좋습니다.
물론 델파이 Menus
단위로는이 작업을 쉽게 수행 할 수 없습니다. 사실 바닐라 형태로 그 유닛으로는 불가능합니다. 이 작업을 수행하려면 해당 단위의 코드를 수정해야합니다. 사용자 정의 메뉴를 그리도록 선택한 코드를 변경해야합니다. 그리고 대신 hbmpItem
에 PARGB32 비트 맵을 생성하고 시스템에 페인트하도록 요청하십시오. 이는 리소스 누출을 피하기 위해 PARGB32 비트 맵의 수명을 관리해야하기 때문에 특히 그렇습니다.
그래서 델파이 7의 아이콘을 사용하여 테마 메뉴를 얻는 방법입니다. 실제로 델파이 6에서이 기능을 구현했지만 코드는 동일합니다. 그리고 심지어 XE3에있는 현재 코드베이스에서도 여전히 동일한 접근법을 사용합니다. 왜? 왜냐하면 나는 VCL 코드를 신뢰하는 것보다 더 많은 메뉴를 그리는 시스템을 신뢰하기 때문이다.
소수의 장소에서 Menus
유닛을 수정해야하기 때문에 코드를 쉽게 공유 할 수 없습니다. 그리고 Menus
코드는 공유 할 수 없습니다. 그러나 필수 요소는 다음과 같습니다.
- Vista 이상에서는 메뉴를 그리지 마십시오. XP에 대해서는 여전히 소유자 그리기가 필요합니다.
- 아이콘의 PARGB32 비트 맵 버전을 만듭니다.
- 이 비트 맵을
hbmpItem
에 넣고 나머지는 시스템에서 처리하도록하십시오.
이것에 대한 아이디어를 찾는 좋은 곳은 Tortoise SVN 소스 코드입니다. 이것은이 문서화되지 않은 기술을 사용하여 테마가있는 글리프 무거운 메뉴를 그립니다.
일부 링크 :
저는 델파이 6 시간 프레임에서 내 코드의 일부를 파고. 나는 여전히 적용 가능하다고 확신한다. 나는이 인터페이스를 선언 오른쪽 Menus
장치의 내 수정 된 버전의 인터페이스 섹션 상단에
:
type
IImageListConvertIconToPARGB32Bitmap = interface
['{4D3E7D64-1288-4D0D-98FC-E61501573204}']
function GetPARGB32Bitmap(ImageIndex: Integer): HBITMAP;
end;
이 이미지 목록 클래스에 의해 구현되고 PARGB32 비트 맵을 제공하는 데 사용됩니다. 그런 다음 TMenuItem.AppendTo
에서 버전이 Vista 이상이고 VCL 코드가 소유자를 그리려는 경우 IsOwnerDraw
을 False
으로 설정합니다. 그리고 IImageListConvertIconToPARGB32Bitmap
을 사용하여 PARGB32
비트 맵을 얻으십시오.
if Supports(GetImageList, IImageListConvertIconToPARGB32Bitmap, Intf) then
begin
BitmapHandle := Intf.GetPARGB32Bitmap(ImageIndex);
if BitmapHandle<>0 then
begin
MenuItemInfo.fMask := MenuItemInfo.fMask or MIIM_BITMAP;
MenuItemInfo.hbmpItem := BitmapHandle;
end;
end;
이미지 목록의 구현은 다음과 같습니다
type
TMyImageList = class(TImageList, IImageListConvertIconToPARGB32Bitmap)
private
FPARGB32BitmapHandles: array of HBITMAP;
procedure DestroyPARGB32BitmapHandles;
function CreatePARGB32BitmapFromIcon(ImageIndex: Integer): HBITMAP;
protected
procedure Change; override;
public
destructor Destroy; override;
function GetPARGB32Bitmap(ImageIndex: Integer): HBITMAP;
end;
destructor TMyImageList.Destroy;
begin
DestroyPARGB32BitmapHandles;
inherited;
end;
function TMyImageList.GetPARGB32Bitmap(ImageIndex: Integer): HBITMAP;
begin
if InRange(ImageIndex, 0, Count-1) then begin
SetLength(FPARGB32BitmapHandles, Count);
if FPARGB32BitmapHandles[ImageIndex]=0 then begin
FPARGB32BitmapHandles[ImageIndex] := CreatePARGB32BitmapFromIcon(ImageIndex);
end;
Result := FPARGB32BitmapHandles[ImageIndex];
end else begin
Result := 0;
end;
end;
procedure TMyImageList.Change;
begin
inherited;
DestroyPARGB32BitmapHandles;
end;
procedure TMyImageList.DestroyPARGB32BitmapHandles;
var
i: Integer;
begin
for i := 0 to high(FPARGB32BitmapHandles) do begin
if FPARGB32BitmapHandles[i]<>0 then begin
DeleteObject(FPARGB32BitmapHandles[i]);
end;
end;
Finalize(FPARGB32BitmapHandles);
end;
type
TWICRect = record
X, Y, Width, Height: Integer;
end;
IWICBitmapSource = interface//only GetSize and CopyPixels have been correctly defined
['{00000120-A8F2-4877-BA0A-FD2B6645FB94}']
function GetSize(out Width, Height: UINT): HResult; stdcall;
function GetPixelFormat: HResult; stdcall;
function GetResolution: HResult; stdcall;
function CopyPalette: HResult; stdcall;
function CopyPixels(const rc: TWICRect; cbStride, cbBufferSize: UINT; Buffer: Pointer): HResult; stdcall;
end;
IWICImagingFactory = interface//only CreateBitmapFromHICON has been correctly defined
['{EC5EC8A9-C395-4314-9C77-54D7A935FF70}']
function CreateDecoderFromFileName: HRESULT; stdcall;
function CreateDecoderFromStream: HRESULT; stdcall;
function CreateDecoderFromFileHandle: HRESULT; stdcall;
function CreateComponentInfo: HRESULT; stdcall;
function CreateDecoder: HRESULT; stdcall;
function CreateEncoder: HRESULT; stdcall;
function CreatePalette: HRESULT; stdcall;
function CreateFormatConverter: HRESULT; stdcall;
function CreateBitmapScaler: HRESULT; stdcall;
function CreateBitmapClipper: HRESULT; stdcall;
function CreateBitmapFlipRotator: HRESULT; stdcall;
function CreateStream: HRESULT; stdcall;
function CreateColorContext: HRESULT; stdcall;
function CreateColorTransformer: HRESULT; stdcall;
function CreateBitmap: HRESULT; stdcall;
function CreateBitmapFromSource: HRESULT; stdcall;
function CreateBitmapFromSourceRect: HRESULT; stdcall;
function CreateBitmapFromMemory: HRESULT; stdcall;
function CreateBitmapFromHBITMAP: HRESULT; stdcall;
function CreateBitmapFromHICON(Icon: HICON; out Bitmap: IWICBitmapSource): HRESULT; stdcall;
function CreateComponentEnumerator: HRESULT; stdcall;
function CreateFastMetadataEncoderFromDecoder: HRESULT; stdcall;
function CreateFastMetadataEncoderFromFrameDecode: HRESULT; stdcall;
function CreateQueryWriter: HRESULT; stdcall;
function CreateQueryWriterFromReader: HRESULT; stdcall;
end;
var
ImagingFactory: IWICImagingFactory;
ImagingFactoryCreationAttempted: Boolean;
function TMyImageList.CreatePARGB32BitmapFromIcon(ImageIndex: Integer): HBITMAP;
const
CLSID_WICImagingFactory: TGUID = '{CACAF262-9370-4615-A13B-9F5539DA4C0A}';
var
Icon: THandle;
Bitmap: IWICBitmapSource;
cx, cy, cbStride, cbBuffer: UINT;
bmi: TBitmapInfo;
bits: Pointer;
begin
Try
Result := 0;
if not Assigned(ImagingFactory) then begin
if ImagingFactoryCreationAttempted then begin
exit;
end;
ImagingFactoryCreationAttempted := True;
if not Succeeded(CoCreateInstance(CLSID_WICImagingFactory, nil, CLSCTX_INPROC_SERVER, IWICImagingFactory, ImagingFactory)) then begin
exit;
end;
end;
Icon := ImageList_GetIcon(Handle, ImageIndex, ILD_NORMAL);
if Icon<>0 then begin
if Succeeded(ImagingFactory.CreateBitmapFromHICON(Icon, Bitmap)) and Succeeded(Bitmap.GetSize(cx, cy)) then begin
ZeroMemory(@bmi, SizeOf(bmi));
bmi.bmiHeader.biSize := SizeOf(bmi.bmiHeader);
bmi.bmiHeader.biPlanes := 1;
bmi.bmiHeader.biCompression := BI_RGB;
bmi.bmiHeader.biWidth := cx;
bmi.bmiHeader.biHeight := -cy;
bmi.bmiHeader.biBitCount := 32;
Result := CreateDIBSection(0, bmi, DIB_RGB_COLORS, bits, 0, 0);
if Result<>0 then begin
cbStride := cx*SizeOf(DWORD);
cbBuffer := cy*cbStride;
if not Succeeded(Bitmap.CopyPixels(TWICRECT(nil^), cbStride, cbBuffer, bits)) then begin
DeleteObject(Result);
Result := 0;
end;
end;
end;
DestroyIcon(Icon);
end;
Except
//none of the methods called here raise exceptions, but we still adopt a belt and braces approach
Result := 0;
End;
end;
재미있는 읽기; 감사합니다 –
@David Heffernan, Embarcadero는 XE6에서 새로운 스타일의 메뉴를 소개합니다. 여기서 논의 된 문제를 해결할 수 있다고 생각한다면 궁금합니다. –
@David 스타일이 지정된 메뉴는 다른 작업을 수행합니다. 비 시스템 스타일로 메뉴를 그립니다. 시스템 스타일 코드는 이전과 동일합니다. 공정하게 말하면 Emba는 시스템 스타일의 메뉴를 통해 결함을 한꺼번에 해결했습니다. 그러나 나는 아직도 그들이 소유자 그림에 의해 그것을 잘못된 길로 생각한다. 나는 그들이 시스템에 메뉴를 그릴 때가 더 일찍하자. –