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

为窗体和按钮创建位图区域

2004年2月9日

2分钟阅读

viewsIcon

335668

downloadIcon

12004

本文档描述了如何为 WinForms 和按钮创建位图区域。

Sample Image - bmprgnform.jpg

引言

本文展示了如何为窗体和按钮创建位图区域。我认为已经有几篇文章讨论了 MFC 中的相关内容,而本文使用了类似的技术,即区域 (Regions)。除了在窗体上使用此技术外,它还允许您将其应用于按钮,并实现悬停效果(即,当鼠标悬停在按钮上时更改另一个图像和/或区域),而无需创建自定义控件。

主要功能概述

以下是用于创建位图区域的两个主要函数的代码。它们位于 BitmapRegion.cs 中。

// Create and apply the given bitmap region on the supplied control
public static void CreateControlRegion(Control control, Bitmap bitmap)
{
    // Return if control and bitmap are null
    if(control == null || bitmap == null)
        return;
            
    // Set our control's size to be the same as the bitmap
    control.Width = bitmap.Width;
    control.Height = bitmap.Height;

    // Check if we are dealing with Form here
    if(control is System.Windows.Forms.Form)
    {
        // Cast to a Form object
        Form form = (Form)control;

        // Set our form's size to be a little larger that the bitmap just 
        // in case the form's border style is not set to none in the first 
        // place
        form.Width += 15;
        form.Height += 35;
        
        // No border
        form.FormBorderStyle = FormBorderStyle.None;

        // Set bitmap as the background image
        form.BackgroundImage = bitmap;

        // Calculate the graphics path based on the bitmap supplied
        GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);

        // Apply new region
        form.Region = new Region(graphicsPath);
    }

    // Check if we are dealing with Button here
    else if(control is System.Windows.Forms.Button)
    {
        // Cast to a button object
        Button button = (Button)control;

        // Do not show button text
        button.Text = "";
                
        // Change cursor to hand when over button
        button.Cursor = Cursors.Hand;

        // Set background image of button
        button.BackgroundImage = bitmap;
                
        // Calculate the graphics path based on the bitmap supplied
        GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);

        // Apply new region
        button.Region = new Region(graphicsPath);
    }
}

// Calculate the graphics path that representing the figure in the bitmap 
// excluding the transparent color which is the top left pixel.
private static GraphicsPath CalculateControlGraphicsPath(Bitmap bitmap)
{
    // Create GraphicsPath for our bitmap calculation
    GraphicsPath graphicsPath = new GraphicsPath();

    // Use the top left pixel as our transparent color
    Color colorTransparent = bitmap.GetPixel(0, 0);

    // This is to store the column value where an opaque pixel is first found.
    // This value will determine where we start scanning for trailing 
    // opaque pixels.
    int colOpaquePixel = 0;

    // Go through all rows (Y axis)
    for(int row = 0; row < bitmap.Height; row ++)
    {
        // Reset value
        colOpaquePixel = 0;

        // Go through all columns (X axis)
        for(int col = 0; col < bitmap.Width; col ++)
        {
            // If this is an opaque pixel, mark it and search 
            // for anymore trailing behind
            if(bitmap.GetPixel(col, row) != colorTransparent)
            {
                // Opaque pixel found, mark current position
                colOpaquePixel = col;

                // Create another variable to set the current pixel position
                int colNext = col;

                // Starting from current found opaque pixel, search for 
                // anymore opaque pixels trailing behind, until a transparent
                // pixel is found or minimum width is reached
                for(colNext=colOpaquePixel; colNext<bitmap.Width; colNext++)
                    if(bitmap.GetPixel(colNext, row) == colorTransparent)
                        break;

                // Form a rectangle for line of opaque pixels found and 
                // add it to our graphics path
                graphicsPath.AddRectangle(new Rectangle(colOpaquePixel, 
                                           row, colNext - colOpaquePixel, 1));

                // No need to scan the line of opaque pixels just found
                col = colNext;
            }
        }
    }

    // Return calculated graphics path
    return graphicsPath;
}

为窗体创建位图区域

要为窗体创建位图区域,您只需要这两行代码。请注意,您无需显式地将窗体的边框更改为无边框,因为系统会自动为您完成此操作。

public class Form1 : System.Windows.Forms.Form
{
    // Load your bitmap for the form
    private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");

    public Form1()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        // Make our bitmap region for the form
        BitmapRegion.CreateControlRegion(this, bmpFrmBack);
    }
}

为按钮创建位图区域

为按钮创建位图区域与为窗体创建位图区域相同。此外,您无需对按钮的样式进行任何更改。

public class Form1 : System.Windows.Forms.Form
{
    // Load your bitmap for the form
    private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
    
    // Load your bitmap for the button
    private Bitmap bmpBob = new Bitmap(typeof(Form1), "bob.bmp");

    public Form1()
    {
        //
        // Required for Windows Form Designer support
        //
        InitializeComponent();

        // Make our bitmap region for the form
        BitmapRegion.CreateControlRegion(this, bmpFrmBack);
        
        // Make our bitmap regions for the buttons
        BitmapRegion.CreateControlRegion(button1, bmpBob);
    }
}

现在,如果您想在鼠标悬停在按钮上时更改按钮的位图,我很高兴地告诉您,您无需为此创建自定义按钮控件。您可以简单地处理如下所示的 MouseLeaveMouseEnter 事件。

private void button1_MouseEnter(object sender, System.EventArgs e)
{
    // Make bitmap region for button
    BitmapRegion.CreateControlRegion(button1, bmpBobSay);
}

private void button1_MouseLeave(object sender, System.EventArgs e)
{
    // Make bitmap region for button
    BitmapRegion.CreateControlRegion(button1, bmpBob);
}

拖动窗体

由于窗体现在没有标题栏,我们需要使其能够让用户在窗体的任何位置单击并拖动它。MFC 版本使用的解决方案是处理 WM_LBUTTONDOWN 并执行 PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y)),以欺骗 Windows 认为我们单击了标题栏。但是,我无法让这在 Windows Forms 中工作,因此以下代码是替代解决方案。

private void Form1_MouseMove(object sender, 
                                        System.Windows.Forms.MouseEventArgs e)
{
    // Check if dragging of the form has occurred
    if(e.Button == MouseButtons.Left)
    {
        // If this is the first mouse move event for left click dragging 
        // of the form, store the current point clicked so that we can use 
        // it to calculate the form's new location in subsequent mouse move 
        // events due to left click dragging of the form
        if(isFirst == true)
        {
            // Store previous left click position
            prevLeftClick = new Point(e.X, e.Y);

            // Subsequent mouse move events will not be treated as first time,
            // until the left mouse click is released or other mouse click 
            // occur
            isFirst = false;
        }

        // On subsequent mouse move events with left mouse click down. 
        // (i.e. During dragging of form)
        else
        {
            // This flag here is to do alternate processing for the form 
            // dragging because it causes serious flicking when u allow 
            // every such events to change the form's location.
            // You can try commenting this out to see what i mean
            if(toBlock == false)
                this.Location = new Point(this.Location.X + e.X - 
                    prevLeftClick.X, this.Location.Y + e.Y - prevLeftClick.Y);

            // Store new previous left click position
            prevLeftClick = new Point(e.X, e.Y);

            // Allow or deny next mouse move dragging event
            toBlock = !toBlock;
        }
    }

    // This is a new mouse move event so reset flag
    else
        isFirst = true;
}

需要注意的问题

当按钮获得焦点时,绘制在按钮上的焦点矩形存在问题。我无法禁用此焦点矩形。因此,我将我的位图略微放大,以便焦点矩形最终绘制在按钮区域之外。请查看下图。

© . All rights reserved.