C#创建不规则窗体的4种方式详解

现在,C#创建不规则窗体不是一件难事,下面总结一下:

一、自定义窗体

一般为规则的图形,如圆、椭圆等。

做法:重写Form1_Paint事件(Form1是窗体的名字),最简单的一种情况如下:

System.Drawing.Drawing2D.GraphicsPath shape = new System.Drawing.Drawing2D.GraphicsPath();
shape.AddEllipse(0,0,this.Height, this.Width);
this.Region = new Region(shape);

即重绘窗体的规则。

二、利用背景图片实现

1. 设置窗体的背景图片,其中背景图片是24位(不包括24)以下的位图(BMP图片),并且要设置TansparencyKey的值,一般为你背景图片的背景色,即创建不规则图片时的底色,一般设为你图片中没有的颜色。

这种做法的不好的地方就是背景图片一定要16位或者更低的,而且还要确保客户端的显示。如果监视器的颜色深度设置大于 24 位,则不管 TransparencyKey 属性是如何设置的,窗体的非透明部分都会产生显示问题。若要避免出现这种问题,请确保“显示”控制面板中的监视器颜色深度的设置小于 24 位。当开发具有这种透明功能的应用程序时,请牢记应使您的用户意识到此问题。

实现步骤如下:

1. 新建windows application

2. 选择窗体,找到BackgroundImage属性,点击打开新的窗口,选择下面的导入资源文件,选择你的不规则的BMP图片

3. 找到窗体的TansparencyKey,将它设置为你背景图片的背景色(如黄色)

4. 找到窗体的FormBorderStyle,将其设置为none,即不显示标题栏

5. 运行

<!--[endif]-->

2. 跟背景图片一样的图形,不过是动态加载,遍历位图以实现不规则窗体。它的原理是这样的,在Form的load事件中写方法使得窗体的描绘区域发生改变。

实现步骤如下:

1. 建立winform应用程序

2. 找到窗体的Load事件,双击进行编辑

3. 编写方法,主要的代码如下:

class BitmapRegion
{
  public BitmapRegion()
  { } 

  /// <summary>
  /// Create and apply the region on the supplied control
  /// 创建支持位图区域的控件(目前有button和form)
  /// </summary>
  /// <param name="control">The Control object to apply the region to控件</param>
  /// <param name="bitmap">The Bitmap object to create the region from位图</param>
  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
    //当控件是form时
    if (control is System.Windows.Forms.Form)
    {
      // Cast to a Form object
      //强制转换为FORM
      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的边界FormBorderStyle不为NONE时,应将FORM的大小设置成比位图大小稍大一点
      form.Width = control.Width;
      form.Height = control.Height;
      // 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
    //当控件是button时
    else if (control is System.Windows.Forms.Button)
    {
      // Cast to a button object
      //强制转换为 button
      Button button = (Button)control;
      // Do not show button text
      //不显示button text
      button.Text = ""; 

      // Change cursor to hand when over button
      //改变 cursor的style
      button.Cursor = Cursors.Hand;
      // Set background image of button
      //设置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);
    }
  }
  /// <summary>
  /// Calculate the graphics path that representing the figure in the bitmap
  /// excluding the transparent color which is the top left pixel.
  /// //计算位图中不透明部分的边界
  /// </summary>
/// <param name="bitmap">The Bitmap object to calculate our graphics path from</param>
  /// <returns>Calculated graphics path</returns>
  private static GraphicsPath CalculateControlGraphicsPath(Bitmap bitmap)
  {
    // Create GraphicsPath for our bitmap calculation
    //创建 GraphicsPath
    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.
    //第一个找到点的X
    int colOpaquePixel = 0;
    // Go through all rows (Y axis)
    // 偏历所有行(Y方向)
    for (int row = 0; row < bitmap.Height; row++)
    {
      // Reset value
      //重设
      colOpaquePixel = 0;
      // Go through all columns (X axis)
      //偏历所有列(X方向)
      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
          //将不透明点加到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;
  }
}

4.运行

<!--[endif]-->

三、调用类库实现

主要就是根据一些坐标,然后根据这些坐标绘制窗体

代码如下:

public Form3()
    {
      InitializeComponent();
      //创建不规则窗体
      POINTAPI[] poin;
      poin = new POINTAPI[5];
      poin[0].x = 90;
      poin[0].y = 90;
      poin[1].x = this.Width;
      poin[1].y = 0;
      poin[2].x = Width;
      poin[2].y = this.Height / 2;
      poin[3].x = Width / 2;
      poin[3].y = Height / 2;
      poin[4].x = 0;
      poin[4].y = Width;
      Boolean flag = true;
      IntPtr hRgn = CreatePolygonRgn(ref poin[0], 8, 1);
      SetWindowRgn(this.Handle, hRgn, ref flag);
      this.BackColor = Color.BurlyWood;
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct POINTAPI
    {
      internal int x;
      internal int y;
    }
    [DllImport("gdi32.dll")]
 private static extern IntPtr CreatePolygonRgn(ref POINTAPI lpPoint,int nCount,int nPolyFillMode);
    [DllImport("user32.dll")]
 private static extern IntPtr SetWindowRgn(IntPtr hWnd,IntPtr hRgn, ref Boolean bRedraw);
    //设置窗体显示状态
    [DllImport("user32.dll")]
private static extern int SetWindowPos(IntPtr hwnd,int hWndInsertAfter,int x,int y,int cx,int cy,int wFlags);
    private void Start_Btn_Click(object sender, EventArgs e)
    {//始终显示在前面
      SetWindowPos(this.Handle, -1, 0, 0, 0, 0, 1);
    }
    private void button1_Click(object sender, EventArgs e)
    {
      //最小化始终显示在前面
      SetWindowPos(this.Handle, -1, 0, 0, 0, 0, 0);
    }

当然,我们也可以自定义窗体的动作,如按着某个轨迹一定,下面的代码中的BackgroundForm程序中就小试了一下,效果还不错,下面是这些程序的效果图:

代码是.Net 2.0的,也可以转换为其他版本的,只要运行主程序即可。

以上的四种方法有利也有弊,希望大家提意见或者更好的解决方案。

(0)

相关推荐

  • C# WinForm打开PDF文件并在窗体中显示

    1.添加引用 工具箱---右键---选择项--COM组件--Adobe PDF Reader 2.使用方法 复制代码 代码如下: OpenFileDialog openFile=new OpenFileDialog(); open..Filter = "PDF文件|*.pdf"; openFile.ShowDialog(); axAcroPDF1.src = openFile.FileName; //axAcroPDF1.LoadFile(of.FileName);   //使用方法二

  • C# WinForm制作异形窗体与控件的方法

    本文实例讲述了C# WinForm制作异形窗体与控件的方法.分享给大家供大家参考,具体如下: 制作异形窗体或控件的思路一般都是想办法生成一个region,然后设置给指定的窗口或控件.生成region的方法有很多,最常用的就是从一幅图片生成,把该图片中的透明色部分"抠"掉,剩下的部分作为一个region.设置窗口或控件的region可以用SetWindowRgn API,不过.NET framework封装了这个操作,在C#中只要对窗口或控件的Region属性赋值就可以了.下面我就把我在

  • 用 C# Winform做出全透明的磨砂玻璃窗体效果代码

    首先, 调用系统 API, 这里如果要引用神马的, 就不一一列出了, 大家自己引用一下. 复制代码 代码如下: [StructLayout(LayoutKind.Sequential)] public struct MARGINS { public int Left; public int Right; public int Top; public int Bottom; } [DllImport("dwmapi.dll", PreserveSig = false)] public s

  • c# winform 关闭窗体时同时结束线程实现思路

    前不久,做一个winform小程序,是给客户导数据用的.当时就发现调试的时候,线程有点问题,到某个点时就走不动了.但是运行确实没有问题的. 只是在关闭窗体后,资源管理器里,一大堆进程. 当时,客户急着用,小测了下,导出数据无误,进程就先不管了. 后来自己去查资料,发现只要在线程那里设置个属性 复制代码 代码如下: Thread th = new Thread(Excute); th.IsBackground = true;这样就解决问题了. 这个属性的意思就是把线程设置为后台线程. 然后关闭进程

  • winform c#中子窗体关闭刷新父窗体的实例

    父窗体Form1 子窗体Form2 Form1中有一个datagridview控件和一添加按钮, Form2中有一个Text控件和一个保存按钮 要求点击Form1窗体上的添加按钮,弹出Form2, 再text里面输入内容,点击保存自动关闭Form2,刷新Form1中datagridview中的数据 From1中:  private void button3_Click(object sender, EventArgs e) { Form2 f2 = new Form2(); f2.ShowDia

  • C# Winform实现捕获窗体最小化、最大化、关闭按钮事件的方法

    本文实例讲述了C# Winform实现捕获窗体最小化.最大化.关闭按钮事件的方法,主要是通过重写WndProc来实现的.分享给大家供大家参考.具体方法如下: 主要功能代码如下: const int WM_SYSCOMMAND = 0x112; const int SC_CLOSE = 0xF060; const int SC_MINIMIZE = 0xF020; const int SC_MAXIMIZE = 0xF030; protected override void WndProc(ref

  • C#中Winform窗体Form的关闭按钮变灰色的方法

    本文实例讲述了C#中Winform窗体Form的关闭按钮变灰色的方法,对C#程序设计有一定的借鉴价值,分享给大家供大家参考之用.具体方法如下: 主要功能代码如下: [ DllImport ( "USER32.DLL" ) ] public static extern int GetSystemMenu(int hwnd, int bRevert); [ DllImport ( "USER32.DLL" ) ] public static extern int Rem

  • C# Winform 子窗体访问父级窗体的控件和属性

    今天在做一个联系人管理的C#设计时,遇到了这个问题,我需要将父窗体中的textBox中的值传到子窗体并进行数据库查询操作,我用了new 父窗体().textBox.text;来进行值传递,然而并无卵用,经过多次试验,找到了一个比较简单的解决方法: 一.子窗体调用父窗体的静态变量 父窗体:Logout 子窗体:Affirm 父窗体文本框:tB_Logout_Username public partial class Logout : Form { //定义一个静态变量存放父窗体中的文本框的值 pu

  • 浅谈C# winForm 窗体闪烁的问题

    在构造函数里加上以下代码: this.DoubleBuffered = true;//设置本窗体 SetStyle(ControlStyles.UserPaint, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // 禁止擦除背景. SetStyle(ControlStyles.DoubleBuffer, true); // 双缓冲 //SetStyle(ControlStyles.DoubleBuffer | Contro

  • C#实现winform用子窗体刷新父窗体及子窗体改变父窗体控件值的方法

    本文实例讲述了C#实现winform用子窗体刷新父窗体及子窗体改变父窗体控件值的方法.分享给大家供大家参考.具体如下: 第一种方法: 用委托,Form2和Form3是同一组 Form2 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows

  • C#,winform,ShowDialog,子窗体向父窗体传值

    调用showdialog方法后,调用代码被暂停执行,等到调用showdialog方法的窗体关系后再继续执行.而且窗体可以返回一个dialogresult值,他描述了窗体关闭的原因,例如OK,Cancel,yes,no等.为了让窗体返回一个dialogresult,必须设置窗体的dialogresult值,或者在窗体的一个按钮上设置dialogresult属性. 例子: 下面是子窗体代码,要求输入phone,然后会返回给父窗体. using System; using System.Collect

随机推荐