修正XPMenu的两个Bug

xiaojianxj520

xiaojianxj520

2016-02-19 16:24

今天图老师小编要跟大家分享修正XPMenu的两个Bug,精心挑选的过程简单易学,喜欢的朋友一起来学习吧!
XPMenu是大名鼎鼎的Dephi第三方界面控件,最近在使用中发现了几个Bug,并对其中的两个进行修正。
  
  1、首先,是绘制菜单和工具栏图标时,会将图像白色部分作为透明色,导致图像缺损非常难看,如下图所示:
  
  查看XPMenu的源代码,图标是通过TXPMenu.DrawIcon函数绘制的,函数内计算了图标显示的位置、调用GrayBitmap、DimBitmap、DrawBitmapShadow等函数对图像进行了处理,并将图像的Transparent设为true,再查看GrayBitmap、DimBitmap、DrawBitmapShadow函数并没有发现会导致透明色计算错误的代码。再往回找,终于在TXPMenu.MenueDrawItem和TXPMenu.ToolBarDrawButton里发现了问题,先来看看TXPMenu.MenueDrawItem:
  

  procedure TXPMenu.MenueDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect;
    Selected: Boolean);
  beign
  .....
    //-------
    if HasBitmap then
      begin
        B.Width := FMenuItem.Bitmap.Width;
        B.Height := FMenuItem.Bitmap.Height;
    // +jt
       //B.Canvas.Brush.Color := FTransparentColor; // ACanvas.Brush.Color;
       B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher" ashert@yadasystems.com
       B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
       FMenuItem.Bitmap.Transparent := true;
       FMenuItem.Bitmap.TransparentMode := tmAuto;
        B.Canvas.Draw(0,0,FMenuItem.Bitmap);
    // +jt
      end;

  

  
    if HasImgLstBitmap then
    begin
    {$IFDEF VER5U}
      if FMenuItem.Parent.SubMenuImages nil then
      begin
        ImgListHandle := FMenuItem.Parent.SubMenuImages.Handle;
        ImgIndex := FMenuItem.ImageIndex;

        B.Width := FMenuItem.Parent.SubMenuImages.Width;
        B.Height := FMenuItem.Parent.SubMenuImages.Height;
       // B.Canvas.Brush.Color := FTransparentColor; // ACanvas.Brush.Color; // +jt
        B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher" ashert@yadasystems.com
  
      B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
        ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent);
  
      end
      else

    {$ENDIF}
      if FMenuItem.Parent.GetParentMenu.Images nil then
      begin
        ImgListHandle := FMenuItem.Parent.GetParentMenu.Images.Handle;
        ImgIndex := FMenuItem.ImageIndex;

        B.Width := FMenuItem.Parent.GetParentMenu.Images.Width;
        B.Height := FMenuItem.Parent.GetParentMenu.Images.Height;
        //B.Canvas.Brush.Color := FTransparentColor; //ACanvas.Pixels[2,2]; // +jt
        B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher" ashert@yadasystems.com
        B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
        ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent);
  

      end;

    end;
  ......
    if
(B nil) and (B.Width 0) then  // X
  
    DrawIcon(FMenuItem, ACanvas, B, IconRect,
        Selected or DrawTopMenuBorder, False, FMenuItem.Enabled, FMenuItem.Checked,
        FTopMenu, FMenu.IsRightToLeft);
  ......
  end;

  很明显,除了XPMenu的作者之外,+jt、Asher和X均对代码进行过修改,问题就出在+jk的修改的这段代码里,它先用B(TBitmap).Canvas.Pixels[0, B.Height - 1]处的颜色填充了整个B,然后再把要显示的图标画到B里去,后面会调用DrawIcon把图标显示出来,可惜+jt计算错误,当时的B(TBitmap).Canvas.Pixels[0, B.Height - 1]值正是$FFFFFF(白色),以ILD_Transparent参数调用ImageList_DrawEx时会把原图标底色(比如上图的粉红色)去掉,那么DrawIcon绘制的图标底色就是白色的,最后图标所有白色部分被挖空了。+jt真是好心做坏事了。
  原因找到了,现在直接把+jt那部分代码去掉即可。原程序变为:
  

  procedure TXPMenu.MenueDrawItem(Sender: TObject; ACanvas: TCanvas; ARect: TRect;
    Selected: Boolean);
  beign
  .....
    //-------
    if HasBitmap then
      begin
        B.Width := FMenuItem.Bitmap.Width;
        B.Height := FMenuItem.Bitmap.Height;
  {Modify: Conch 2004-12-16 画出的图标透明颜色有错
    // +jt
       //B.Canvas.Brush.Color := FTransparentColor; // ACanvas.Brush.Color;
       B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher"
ashert@yadasystems.com
       B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
       FMenuItem.Bitmap.Transparent := true;
       FMenuItem.Bitmap.TransparentMode := tmAuto;
  }
  
      B.Canvas.Draw(0,0,FMenuItem.Bitmap);
    // +jt
      end;

  

  
    if HasImgLstBitmap then
    begin
    {$IFDEF VER5U}
      if FMenuItem.Parent.SubMenuImages nil then
      begin
        ImgListHandle := FMenuItem.Parent.SubMenuImages.Handle;
        ImgIndex := FMenuItem.ImageIndex;

        B.Width := FMenuItem.Parent.SubMenuImages.Width;
        B.Height := FMenuItem.Parent.SubMenuImages.Height;
  {Modify: Conch 2004-12-16 画出的图标透明颜色有错
       // B.Canvas.Brush.Color := FTransparentColor; // ACanvas.Brush.Color; // +jt
        B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher"
ashert@yadasystems.com
        B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
        ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent);
  }
  
      ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_NORMAL);
  //Conch
  
  
      end
      else

    {$ENDIF}
      if FMenuItem.Parent.GetParentMenu.Images nil then
      begin
        ImgListHandle := FMenuItem.Parent.GetParentMenu.Images.Handle;
        ImgIndex := FMenuItem.ImageIndex;

        B.Width := FMenuItem.Parent.GetParentMenu.Images.Width;
        B.Height := FMenuItem.Parent.GetParentMenu.Images.Height;
  {Modify: Conch 2004-12-16 画出的图标透明颜色有错
        //B.Canvas.Brush.Color := FTransparentColor; //ACanvas.Pixels[2,2]; // +jt
        B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher"
ashert@yadasystems.com
        B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
        ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent);
  }
  
      ImageList_DrawEx(ImgListHandle, ImgIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_NORMAL);
  //Conch
  
  

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

      end;

    end;
  ......
    if
(B nil) and (B.Width 0) then  // X
  
    DrawIcon(FMenuItem, ACanvas, B, IconRect,
        Selected or DrawTopMenuBorder, False, FMenuItem.Enabled, FMenuItem.Checked,
        FTopMenu, FMenu.IsRightToLeft);
  ......
  end;

  TXPMenu.ToolBarDrawButton的原因也是一样。
  

  procedure TXPMenu.ToolBarDrawButton(Sender: TToolBar;
    Button: TToolButton; State: TCustomDrawState; var DefaultDraw: Boolean);
  begin
        if CanDraw then
        begin {CanDraw}
  
{Modify: Conch 2004-12-16  画出的图标透明颜色有错
         // B.Canvas.Brush.Color := TransparentColor; // ACanvas.Brush.Color; // +jt
          B.Canvas.Brush.Color := B.Canvas.Pixels[0, B.Height - 1];//"Todd Asher"
ashert@yadasystems.com
          B.Canvas.FillRect(Rect(0, 0, B.Width, B.Height));
          ImageList_DrawEx(ImglstHand, Button.ImageIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_Transparent);
  }

          ImageList_DrawEx(ImglstHand, Button.ImageIndex,
          B.Canvas.Handle, 0, 0, 0, 0, clNone, clNone, ILD_NORMAL);
  //Conch
  ......
  end;

  
  2、第二个问题是菜单的阴影太呆板,是固定的一层灰色块(当然是指WinXP以前的系统),如图:
  
  
  菜单阴影是由TXPMenu.DrawWindowBorder函数绘制的。这里可以看到+jt进行过修改,不过+jt的注释方式有点特别,看不出那些在作者的原代码,那些是+jt改过的,不过可以肯定+jt下了不少苦功。这个函数把Windows来的那个3D边框去掉,变成平面的OfficeXP风格菜单。若果要改为像WinXP菜单的那种通过与背景像素混合的得到的阴影效果,必须对源码进行大改,但使用第三方控件目的是为了方便省事,如果那样做的话就太有违初衷了。因此我这里用了一种折衷的办法,把灰色面积减少,这样看起来就顺眼多了。最终效果如下图:
  
  修改后的TXPMenu.DrawWindowBorder函数:
  procedure TXPMenu.DrawWindowBorder(hWnd: HWND; IsRightToLeft: boolean);
  var
    WRect: TRect;
    dCanvas: TCanvas;
    wDC: HDC; // +jt

  

   regiontype: integer; // +jt
  
 r1,r2,wr,region: HRGN; // +jt
  
 rgnr: TRect; // +jt
  
begin

    if (hWnd = 0) or (FSettingWindowRng) then
   begin
    exit;
   end;
  // +jt
   wDC := GetWindowDC(hWnd); //GetDesktopWindow
  
 if wDC=0 then exit;
  // +jt
    FSettingWindowRng :=true; // +jt
   dCanvas := TCanvas.Create;
   try
     dCanvas.Handle := wDC; // +jt
      GetWindowRect(hWnd, WRect);
    // +jt
     WRect.Right := WRect.Right-WRect.Left;
     WRect.Bottom := WRect.Bottom-WRect.Top;
     WRect.Top:=0;
     WRect.Left:=0;
      if GetWindowLong(hWnd,GWL_WNDPROC)integer(@MenuWindowProc) then
     begin
       SetWindowLong(hWnd,GWL_USERDATA,GetWindowLong(hWnd,GWL_WNDPROC));
       SetWindowLong(hWnd,GWL_WNDPROC,integer(@MenuWindowProc));
     end;
  {Modify: Conch 2004-11-04 画出的阴影太难看了
      if not IsWXP then
     begin
        wr:= CreateRectRgn(0,0,0,0);
       regiontype := GetWindowRgn(hWnd, wr);
       GetRgnBox(wr,rgnr);
       DeleteObject(wr);
        if (regionType = ERROR) or (abs(rgnr.Right-WRect.Right)5) or (abs(rgnr.Bottom-WRect.Bottom)5) then
       begin
         region:= CreateRectRgn(0,0,0,0);
         r1:=CreateRectRgn(WRect.Left,WRect.Top,WRect.Right-2,WRect.Bottom-2);
         r2:=CreateRectRgn(WRect.Left+2,WRect.Top+2,WRect.Right,WRect.Bottom);
         CombineRgn(region,r1,r2,RGN_OR);
         DeleteObject(r1);
         DeleteObject(r2);
          SetWindowRgn(hWnd,region,true);
        end;
   // +jt
        Dec(WRect.Right, 2);
       Dec(WRect.Bottom, 2);
     end; // +jt
  }

      dCanvas.Brush.Style := bsClear;
      dCanvas.Pen.Color := FMenuBorderColor;
     dCanvas.Rectangle(WRect.Left, WRect.Top, WRect.Right, WRect.Bottom);
      if IsRightToLeft then
     begin
        dCanvas.Pen.Color := FFIconBackColor;
       dCanvas.MoveTo(WRect.Right - 3, WRect.Top + 2);
       dCanvas.LineTo(WRect.Right - 2, WRect.Bottom - 1);
      end
     else
     begin
  
     dCanvas.Pen.Color := FFIconBackColor;
       dCanvas.Rectangle(WRect.Left + 1, WRect.Top + 2, WRect.Left + 3, WRect.Bottom - 1);
     end;
  // +jt
         StretchBlt(dCanvas.Handle,WRect.Left + 1,WRect.Top + 1,WRect.Right - WRect.Left-1,2,
                    dCanvas.Handle,WRect.Left + 1,WRect.Top + 3,WRect.Right - WRect.Left-1,1,SRCCOPY);
          if IsWXP then
         begin
           StretchBlt(dCanvas.Handle,WRect.Left + 1,WRect.Bottom - 3,WRect.Right - WRect.Left-1,2,
                      dCanvas.Handle,WRect.Left + 1,WRect.Top + 3,WRect.Right - WRect.Left-1,1, SRCCOPY);
           dCanvas.Pen.Color := FFColor;
           dCanvas.Rectangle(WRect.Right - 3, WRect.Top+1, WRect.Right - 1, WRect.Bottom-1);
         end;
  // +jt
  {Modify: Conch 2004-11-04 画出的阴影太难看了
      Inc(WRect.Right, 2);
     Inc(WRect.Bottom, 2);
      if not IsWXP then // +jt
     begin // +jt
       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);
     end; // +jt
  }

   finally
     ReleaseDC(hWnd, wDC); // +jt
     dCanvas.Free;
   FSettingWindowRng :=false;
   end;

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)

  end;

展开更多 50%)
分享

猜你喜欢

修正XPMenu的两个Bug

编程语言 网络编程
修正XPMenu的两个Bug

Delphi中两个BUG的分析与修复

编程语言 网络编程
Delphi中两个BUG的分析与修复

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

腌肉的两个吃法

营养价值
腌肉的两个吃法

两个世界攻略

电脑网络
两个世界攻略

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

故事两个朋友

故事
故事两个朋友

两个男人正好

心理健康 婚姻保鲜
两个男人正好

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

Observer模式深度探索

Observer模式深度探索

看看大侠是如何解决开机“按F1进去xp系统”的技巧

看看大侠是如何解决开机“按F1进去xp系统”的技巧
下拉加载更多内容 ↓