2014-04-23 2 views
15

나는 델파이 7을 사용하고 있습니다. Windows 7에서 이것을 테스트하십시오.델파이에서 가능한 아이콘으로 테마 메인 메뉴가 있습니까?

TMainMenuTImageList을 양식에 내려 놓으십시오. 일부 메뉴는 TMainMenu에 추가하고 일부 이미지는 TImageList에 추가하십시오. TImageListTMainMenu에 할당되지 않은 경우의 Images 특성, 응용 프로그램은 다음과 같습니다

Delphi themed TMainMenu without icons

그러나 TImageListTMainMenu에 할당되면의 Images 특성, 응용 프로그램은 다음과 같습니다를 :

Delphi non-themed TMainMenu with icons

Images 속성이 변경 될 경우 더 나아가, 런타임에만 메뉴를로 (할당 또는 할당 해제) 디자인 변경시 루트 메뉴 항목 (파일, 편집, 도구, 설정 및 도움말)이 변경되지 않습니다. Images 속성이 디자인 타임에 할당되지 않은 경우 항상 테마가 유지됩니다. Images 속성은 디자인 타임에 할당되었습니다.

마지막으로 XPManifest의 사용 여부와 상관없이이 모든 일이 발생합니다.

그래서, 내 질문은 :

1. 왜 아이콘이 사용되는 경우 사라지는 테마? 아이콘은 Owner Drawing과 같은 것을 사용하여 내부적으로 그려지는 것으로 추측 할 수 있습니다. 이는 테마를 깨뜨린 것이지만 그것은 단지 추측입니다.

2. XPManifest을 사용하지 않은 경우에도 주 메뉴가 테마가 된 이유는 무엇입니까?

3. 가장 중요한 것은 아이콘이있는 테마 메뉴를 어떻게 만들 수 있습니까?

답변

18

이 답변이 너무 많은 호언 장담으로 나오지 않았 으면 좋겠지 만, 이것은 엠바 카데로가 오랫동안 오판의 역사를 가지고있는 분야입니다. 나는이 분야에서 많은 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 코드는 공유 할 수 없습니다. 그러나 필수 요소는 다음과 같습니다.

  1. Vista 이상에서는 메뉴를 그리지 마십시오. XP에 대해서는 여전히 소유자 그리기가 필요합니다.
  2. 아이콘의 PARGB32 비트 맵 버전을 만듭니다.
  3. 이 비트 맵을 hbmpItem에 넣고 나머지는 시스템에서 처리하도록하십시오.

이것에 대한 아이디어를 찾는 좋은 곳은 Tortoise SVN 소스 코드입니다. 이것은이 문서화되지 않은 기술을 사용하여 테마가있는 글리프 무거운 메뉴를 그립니다.

일부 링크 :


저는 델파이 6 시간 프레임에서 내 코드의 일부를 파고. 나는 여전히 적용 가능하다고 확신한다. 나는이 인터페이스를 선언 오른쪽 Menus 장치의 내 수정 된 버전의 인터페이스 섹션 상단에

:

type 
    IImageListConvertIconToPARGB32Bitmap = interface 
    ['{4D3E7D64-1288-4D0D-98FC-E61501573204}'] 
    function GetPARGB32Bitmap(ImageIndex: Integer): HBITMAP; 
    end; 

이 이미지 목록 클래스에 의해 구현되고 PARGB32 비트 맵을 제공하는 데 사용됩니다. 그런 다음 TMenuItem.AppendTo에서 버전이 Vista 이상이고 VCL 코드가 소유자를 그리려는 경우 IsOwnerDrawFalse으로 설정합니다. 그리고 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; 
+0

재미있는 읽기; 감사합니다 –

+0

@David Heffernan, Embarcadero는 XE6에서 새로운 스타일의 메뉴를 소개합니다. 여기서 논의 된 문제를 해결할 수 있다고 생각한다면 궁금합니다. –

+0

@David 스타일이 지정된 메뉴는 다른 작업을 수행합니다. 비 시스템 스타일로 메뉴를 그립니다. 시스템 스타일 코드는 이전과 동일합니다. 공정하게 말하면 Emba는 시스템 스타일의 메뉴를 통해 결함을 한꺼번에 해결했습니다. 그러나 나는 아직도 그들이 소유자 그림에 의해 그것을 잘못된 길로 생각한다. 나는 그들이 시스템에 메뉴를 그릴 때가 더 일찍하자. –