65.9K
CodeProject 正在变化。 阅读更多。
Home

Google Talk 风格的 Windows 窗体

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (34投票s)

2006年5月9日

CPOL

2分钟阅读

viewsIcon

230533

downloadIcon

5367

如何使用自定义的 Paint 事件处理程序来绘制你自己的 Windows 窗体。

Sample Image - GoogleTalkWindowsForm.gif

引言

本文主要解释了如何使用自定义的 Paint 事件处理程序来绘制你自己的 Windows 控件(在本例中是一个 Form 控件)。GoogleTalkForm 类继承并扩展了 System.Windows.Forms.Form 控件 (由 Microsoft .NET Framework 提供) 以提供 Google Talk Windows 窗体的外观和感觉。

类属性 IsWindowSnappable, IsResizable, ResizableColor, TitleColor, TitleFont, TitleBackColor, TitleForeColor, TitleStyle, BodyBackColor, BodyForeColor, BodyStyle, OutlineColor, OutlineSize, IconsNormalColor, IconsHiLiteColor, MinimumHeight, 和 MinimumWidth 可以用来改变窗体的标准外观和行为。

GoogleTalkForm 类提供

  • Windows 窗体的绘制。
  • 使用双缓冲的绘制。
  • 窗体捕捉到特定的客户端桌面区域。
  • 鼠标事件处理。
  • 系统上下文菜单项绘制。

工作原理

首先,我们需要重写窗体事件,例如 OnPaintOnMouseDownOnMouseUp,和 OnMouseMove, OnDoubleClick 来处理窗体 Paint 事件和鼠标事件。

protected override void OnPaint(PaintEventArgs e)
{
   ...
}

protected override void OnMouseDown(MouseEventArgs e)
{
   ...
}

窗体的样式必须使用 GoogleTalkForm 类构造函数中的 SetStyle 方法设置,以反映所需的行为。

this.SetStyle(ControlStyles.AllPaintingInWmPaint | 
              ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.StandardClick, true);
this.SetStyle(ControlStyles.StandardDoubleClick, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.SupportsTransparentBackColor, false);
this.UpdateStyles();

isMousePointerInArea 方法用于检查当前鼠标位置是否在特定的客户端矩形区域内。必须计算出鼠标相对于 Windows 窗体的位置,因为 Control.MousePosition 返回的是相对于桌面的鼠标位置。

private bool isMousePointerInArea(Point mousePosition, Rectangle area)
{
    Point relativePoint = new Point(0, 0);
    relativePoint.X = mousePosition.X - this.Location.X;
    relativePoint.Y = mousePosition.Y - this.Location.Y;

    return area.Contains(relativePoint);
}

自定义窗体绘制全部由 OnFormPaint 方法完成,绘制到一个新创建的 Bitmap 对象上,该对象具有窗体的确切宽度和高度。 从 Bitmap 创建一个 Graphics 对象,并使用诸如 DrawStringDrawImageDrawLineDrawArc 等方法。 一旦所有绘制完成,缓冲的位图就会使用 DrawImageUnscaled 方法复制到窗体图形实例上。

// Create a new Pen object
p = new Pen(this.OutlineColor, this.OutlineSize);

// Draw the form outline
g.DrawArc(p, rectLeftCorner, 180, 90);
g.DrawArc(p, rectRightCorner, 270, 90);
g.DrawLine(p, edgeRadius, 0, this.Width - edgeRadius, 0);
g.DrawLine(p, 0, edgeRadius, 0, this.Height);
g.DrawLine(p, this.Width - 1, edgeRadius, 
           this.Width - 1, this.Height);
g.DrawLine(p, 0, this.Height - 1, 
           this.Width, this.Height - 1);

// Dispose the Pen object
p.Dispose();
p = null;

创建一个自定义的 Region 并将其应用于窗体,以产生圆角边缘效果。 为了创建所需的区域,我们必须逐像素迭代,并将一个 1x1 的矩形添加到 GraphicsPath 对象(该对象将用于创建 Region 对象),当当前选定的像素的颜色与透明颜色不同时。 为了优化代码,仅检查窗体的顶角是否存在透明颜色的像素。

// Create GraphicsPath to be used to crop the region required
gpRegion = new GraphicsPath();

// Loop through every pixel in the top left corner.
// Create a 1 x 1 rectangle regions of pixels
// that do not match the transparent color
for (int x = rectLeftCorner.X; x < rectLeftCorner.Width; x++)
{
    for (int y = rectLeftCorner.Y; y < rectLeftCorner.Height / 2; y++) 
    {
        if (isSameColor(bmp.GetPixel(x, y), 
            this.transparentColor) == false) 
        {
            gpRegion.AddRectangle(new Rectangle(x, y, 1, 1));
        }
    }
}

// Loop through every pixel in the top right corner.
// Create a 1 x 1 rectangle regions of pixels
// that do not match the transparent color
for (int x = rectRightCorner.X + 1; x < 
             rectRightCorner.X + 
             rectRightCorner.Width + 1; x++)
{
    for (int y = rectRightCorner.Y; y < 
         rectRightCorner.Y + rectRightCorner.Height / 2; y++) 
    {
        if (isSameColor(bmp.GetPixel(x, y), 
            this.transparentColor) == false) 
        {
            gpRegion.AddRectangle(new Rectangle(x, y, 1, 1));
        }
    }
}

// Create the remaining rectangular regions
// to complete cover all the windows form area
gpRegion.AddRectangle(new Rectangle(rectLeftCorner.Width, 0, 
         this.Width - (edgeRadius * 4), rectLeftCorner.Height / 2));
gpRegion.AddRectangle(new Rectangle(0, 
         rectLeftCorner.Height / 2, this.Width, this.Height));

// Apply region
this.Region = new Region(gpRegion);

历史

  • 1.0 - 首次发布 - (2006年5月9日)。
  • 1.1 - (2006年5月11日)。
    • 创建了一个窗体设计器来控制窗体(和子控件)的行为。
    • 修复了托管在 MDI 容器中的窗体的行为。
© . All rights reserved.