C# 控件透明背景(winform)

原文出自:C# 实现真正的透明控件(Windows桌面程序)_yangshengchuan的博客-CSDN博客_c# 控件透明

修改了一点bug和方式,现在可以根据任意指定颜色镂空,还可以一次性镂空多个颜色

使用方法:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace UControlTran
{
    public partial class UserControl1 : UserControl
        {

        /************************************************************************************************/
        /******** 构造函数里画图用的文本text必须跟 OnPaint 里面的 text 一模一样,否则文字就错位了*********/
        /******** 自己修改一下构造函数,可以把text作为参数传入,然后保存,OnPaint 直接调用保存的   *********/
        /************************************************************************************************/

        private const TextFormatFlags textFormatFlags = TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix | TextFormatFlags.PreserveGraphicsClipping;
        public UserControl1()
        {
            InitializeComponent();
            var bit = new Bitmap(1, 1);
            var g = Graphics.FromImage(bit);
            var text = "关于其他双缓存,半透明,请参考其他相关知识";
            Size size = TextRenderer.MeasureText(g, text, this.Font, new Size(-1, 0), textFormatFlags);
            TextSize = size;//长宽要保留下来,下面 OnPaint 需要用到
            g.Dispose();
            var bitmap = new Bitmap(size.Width, size.Height);
            g = Graphics.FromImage(bitmap);
            //首先要明白,这里用text来画,并不是就把text画到控件上了
            //而是参照图片画一个跟文字形状一样的图片!!!!!!!!!!!!!!
            //所以要在 OnPaint 重新画文字
            g.DrawString(text, this.Font, Brushes.Red,-1,0);//随手用 text 画了一个测试图片
            g.Dispose();
            Region = APub.ImageToRegionPx(bitmap, Color.FromArgb(0, 0, 0, 0));//将所有透明的区域全部镂空.这样控件里面就不会显示上面 text 内容以外的区域
            bitmap.Dispose();
            }

        public Size TextSize { get; set; }
        /// <summary>
        /// 重写OnPaint方法
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
            {
            var text = "关于其他双缓存,半透明,请参考其他相关知识";
            base.OnPaint(e);
            var g = e.Graphics;
            var brush = new SolidBrush(Color.Red);
            //为什么要在这里再画一次文本,是因为构造函数里面的方法只是以文字的像素/点阵信息为矩阵,画了一个跟文字一样的控件出来,
            //所以颜色是黑色的,那么要实现自己的颜色就必须真正的把颜色文字绘制到控件.
            g.DrawString(text, this.Font, brush, -1, 0);
            brush.Dispose();
            g.Dispose();
            Size = TextSize;
            }
        protected override void OnMouseHover(EventArgs e)
            {
            this.Cursor = Cursors.SizeAll;
            base.OnMouseHover(e);
            }
        protected override void OnMouseLeave(EventArgs e)
            {
            this.Cursor = Cursors.Default;
            base.OnMouseLeave(e);
            }
        }
}

//镂空方法


        /// <summary>
        /// 从图片中获取除指定颜色以外的色块区域图.同时对png透明兼容.
        /// 这个要在自定义控件
        /// </summary>
        /// <param name="Picture">取其区域的图片。</param>
        /// <param name="TransparentColor">要镂空的颜色,必须是ARGB,必须带A,
        /// 因为如果是黑色,rgb=0,0,0,如果没有A通道,黑色就直接被过滤掉了.所以如果要过滤png图片的透明层,就要填Color.FromArgb(0, 0, 0,0)</param>
        /// <returns>图片中非透明色部分的区域</returns>
        public unsafe static Region ImageToRegionPx(Image Picture, Color TransparentColor)
            {
            return ImageToRegionPx(Picture, new List<Color>() { TransparentColor });
            }

        /// <summary>
        /// 从图片中获取除指定颜色以外的色块区域图.同时对png透明兼容
        /// </summary>
        /// <param name="Picture">取其区域的图片。</param>
        /// <param name="keyColors">要镂空的颜色,必须是ARGB,必须带A,
        /// 因为如果是黑色,rgb=0,0,0,如果没有A通道,黑色就直接被过滤掉了.所以如果要过滤png图片的透明层,就要填Color.FromArgb(0, 0, 0,0)</param>
        /// <returns>图片中非透明色部分的区域</returns>
        public unsafe static Region ImageToRegionPx(Image Picture, List<Color> keyColors)
            {
            if (Picture == null) return null;
            Region rgn = new Region();
            rgn.MakeEmpty();

            Bitmap bitmap = null;
            if (Picture.GetType() != typeof(Bitmap))
                bitmap = new Bitmap(Picture);
            else
                bitmap = (Bitmap)Picture;

            int width = bitmap.Width;
            int height = bitmap.Height;
            BitmapData bmData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            byte* p = (byte*)bmData.Scan0;
            int offset = bmData.Stride - width * 4;


            Rectangle curRect = new Rectangle();
            curRect.Height = 1;

            int start = -1;
            // 行座标 ( Y ) 
            for (int Y = 0; Y < height; Y++)
                {
                // 列座标 ( X ) 
                for (int X = 0; X < width; X++)
                    {
                    ///如果像素色彩不是列表中要排除的颜色
                    ///p[3] > 0 如果不透明度是0,就说明是空白像素,强制排除
                    if (start == -1 && p[3] > 0 && keyColors.All(argb => p[3] > argb.A || p[2] != argb.R || p[1] != argb.G || p[0] != argb.B))
                        {
                        start = X;                            //记录这个点
                        curRect.X = X;
                        curRect.Y = Y;
                        }

                    //如果当前像素是要排除的颜色,将 start 要复位
                    else if (start > -1 && keyColors.Any(argb => p[3] <= argb.A && p[2] == argb.R && p[1] == argb.G && p[0] == argb.B))
                        {
                        curRect.Width = X - curRect.X;
                        rgn.Union(curRect);
                        start = -1;
                        }

                    if (X == width - 1 && start > -1)        //如果 之前的点是不透明 且 是最后一个点
                        {
                        curRect.Width = X - curRect.X;
                        rgn.Union(curRect);
                        start = -1;
                        }
                    p += 4;//下一个内存地址
                    }
                p += offset;
                }
            bitmap.UnlockBits(bmData);
            bitmap.Dispose();
            return rgn;
            }

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值