WindowsForm--Bubble User Control

本文介绍了一种自定义的Windows窗体控件——气泡控件,该控件可以根据内容自动调整大小,并具备圆角和指向箭头的特性。支持多行文本显示,能够通过属性设置背景色、字体等样式。

创建一个自定义用户控件,拖入一个label:lblWords,和一个richTextBox:txtWords

代码:

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

namespace WinBubble
{
    public partial class ucBubble : UserControl
    {
        #region 属性字段
        private Color gdiBackColor;//GDI+绘制的背景色-->如果和txt背景色不一致,在文本框和GDI+背景色之间会形成一个边框效果
        private Color txtBackColor;//多行文本框的背景色
        private Color foreColor;//多行文本框的前景色
        private Font font;//字体
        private string words;//文本
        private string direction;//气泡箭头方向
        private int rowsCount = 0;//多行文本框的行数-->估算值 

        private const int X = 6;//气泡箭头三角形的高
        private const int Round = 8;//圆角半径
        private const int M = 5;//文本框和uc边框的距离

        public string CurrentText { get { return txtWords.Text.Replace("\n", ""); } }//文本框的内容
        public string SelectedText { get { return txtWords.SelectedText; } }//选中的内容
        #endregion

        #region 接口
        public ucBubble(int width, Color gdiBackColor, Color txtBackColor, Color foreColor, int fontSize, string fontFamily, string words, string direction)
        {
            InitializeComponent();

            this.Width = width;
            this.gdiBackColor = gdiBackColor;
            this.txtBackColor = txtBackColor;
            this.foreColor = foreColor;
            this.font = new Font(new FontFamily(string.IsNullOrEmpty(fontFamily) ? "微软雅黑" : fontFamily), fontSize);
            this.words = words;
            this.direction = direction == "right" ? "right" : "left";

            //内容文本框
            txtWords.BorderStyle = BorderStyle.None;
            txtWords.ScrollBars = RichTextBoxScrollBars.None;
            txtWords.ImeMode = ImeMode.OnHalf;
            txtWords.BackColor = txtBackColor;
            txtWords.ForeColor = foreColor;
            txtWords.Text = words;
            txtWords.Font = font;

            //lblWords标签的作用是用来判断文本框内容是不是多行的,因为单行内容需要根据宽度进行定位。如果是单行内容,就让文本框的宽度和标签的宽度相等
            lblWords.Visible = false;
            lblWords.Location = new Point(X + M, 0);
            lblWords.Text = "";
            lblWords.Font = font;

            //事件
            this.Paint += new PaintEventHandler(ucBubble_Paint);
            this.MouseWheel += new MouseEventHandler(ucBubble_MouseWheel);
            this.MouseClick += new MouseEventHandler(ucBubble_MouseClick);
            this.DoubleClick += new EventHandler(ucBubble_DoubleClick);

            txtWords.MouseWheel += new MouseEventHandler(ucBubble_MouseWheel);
            txtWords.MouseClick += new MouseEventHandler(ucBubble_MouseClick);
            txtWords.DoubleClick += new EventHandler(ucBubble_DoubleClick);
            txtWords.KeyPress += new KeyPressEventHandler(txtWords_KeyPress);

            rowsCount = GetRowsCount();
            ControlRichTextBox();
            SelectWords(0, 0);
        }

        //从文本中找到匹配的内容,然后改变前景色或者背景色,返回值是txtWords中匹配的个数。foreColorIndex和backColorIndex用来控制只改其中指定的一个匹配内容的颜色。
        public int SelectSomething(string something, Color[] changeForeColor = null, int foreColorIndex = 0, Color[] changeBackColor = null, int backColorIndex = 0)
        {
            int count = 0;//匹配个数
            int start = txtWords.Find(something, 0, RichTextBoxFinds.None);//匹配内容开始索引

            if (start >= 0)//存在匹配内容
            {
                count++;
                txtWords.SelectionStart = start;
                txtWords.SelectionLength = something.Length;
                if (changeForeColor != null && changeForeColor.Length > 0)
                {
                    if (foreColorIndex <= 0 || (foreColorIndex > 0 && foreColorIndex == count))
                    {
                        txtWords.SelectionColor = changeForeColor[0];//改变前景色
                    }
                }
                if (changeBackColor != null && changeBackColor.Length > 0)
                {
                    if (backColorIndex <= 0 || (backColorIndex > 0 && backColorIndex == count))
                    {
                        txtWords.SelectionBackColor = changeBackColor[0];//改变背景色
                    }
                }

                //匹配下一个
                while (txtWords.Text.Length > start + something.Length)
                {
                    start = txtWords.Find(something, start + something.Length, RichTextBoxFinds.None);
                    if (start >= 0)
                    {
                        count++;
                        txtWords.SelectionStart = start;
                        txtWords.SelectionLength = something.Length;

                        if (changeForeColor != null && changeForeColor.Length > 0)
                        {
                            if (foreColorIndex <= 0 || (foreColorIndex > 0 && foreColorIndex == count))
                            {
                                txtWords.SelectionColor = changeForeColor[0];//改变前景色
                            }
                        }
                        if (changeBackColor != null && changeBackColor.Length > 0)
                        {
                            if (backColorIndex <= 0 || (backColorIndex > 0 && backColorIndex == count))
                            {
                                txtWords.SelectionBackColor = changeBackColor[0];//改变背景色
                            }
                        }
                    }
                    else
                        break;
                }
            }

            //返回匹配个数
            return count;
        }

        //还原富文本框的颜色
        public void ClearSomething()
        {
            txtWords.SelectionStart = 0;
            txtWords.SelectionLength = txtWords.Text.Length;
            txtWords.SelectionColor = foreColor;
            txtWords.SelectionBackColor = txtBackColor;
        }

        //选中内容
        public void SelectWords(int start, int length)
        {
            txtWords.Select(start, length);
        }
        #endregion

        #region 方法
        //获取文本内容占用的行数
        private int GetRowsCount()
        {
            int count = 1;
            char[] chars = words.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                lblWords.Text = lblWords.Text + chars[i];
                if (X + M + lblWords.Width + M > this.Width)
                {
                    lblWords.Text = "";
                    i--;
                    count++;
                }
            }
            return count;
        }

        //控制文本框的位置和大小
        private void ControlRichTextBox()
        {
            if (rowsCount > 1)
                txtWords.Width = this.Width - X - M - M; //文本框的宽度=控件宽度-箭头高-左侧留白-右侧留白
            else
                txtWords.Width = lblWords.Width;

            txtWords.Height = rowsCount * font.Height;
            //调整误差
            if (font.Size == 9)
                txtWords.Height += rowsCount * 7;
            else if (font.Size == 12)
                txtWords.Height += rowsCount * 7;
            else if (font.Size == 16)
                txtWords.Height += rowsCount * 8;
            else if (font.Size == 18)
                txtWords.Height += rowsCount * 9;
            else if (font.Size == 20)
                txtWords.Height += rowsCount * 10;
            else
                txtWords.Height += rowsCount * (font.Height / 4);

            //整个uc的高度
            this.Height = txtWords.Height + M + M;

            //位置
            if (this.direction == "left")
            {
                txtWords.Location = new Point(X + M, M);
            }
            else
            {
                if (rowsCount > 1)
                    txtWords.Location = new Point(M, M);
                else
                    txtWords.Location = new Point(this.Width - lblWords.Width - X - M, M);
            }
        }

        //绘制箭头和圆角:气泡箭头在左侧
        private void DrawBubbleLeft(Graphics graphics, Color c)
        {
            SolidBrush brush = new SolidBrush(c);//定义画刷
            int lblMax = X + M + lblWords.Width + M;

            if (rowsCount > 1)
            {
                //背景
                Point[] points = new Point[]
                {
                    //左上角
                    new Point(X,Round),
                    new Point(X+Round,0),
                    //右上角
                    new Point(this.Width-Round,0),
                    new Point(this.Width,Round),
                    //右下角
                    new Point(this.Width,this.Height-Round),
                    new Point(this.Width -Round,this.Height),
                    //左下角
                    new Point(X+Round ,this.Height),
                    new Point(X,this.Height-Round)
                };
                graphics.FillPolygon(brush, points);

                //绘制圆角
                graphics.FillEllipse(brush, X, 0, Round * 2, Round * 2);//左上圆角
                graphics.FillEllipse(brush, this.Width - Round * 2, 0, Round * 2, Round * 2);//右上圆角
                graphics.FillEllipse(brush, this.Width - Round * 2, this.Height - Round * 2, Round * 2, Round * 2);//右下圆角
                graphics.FillEllipse(brush, X, this.Height - Round * 2, Round * 2, Round * 2);//左下圆角

                //三角形
                Point[] points2 = new Point[]
                {
                    new Point(X,12),
                    new Point( 0,17),
                    new Point(X,22)
                };
                graphics.FillPolygon(brush, points2);
            }
            else
            {
                //背景
                Point[] points = new Point[]
                {
                    //左上角
                    new Point(X,Round),
                    new Point(X+Round,0),
                    //右上角
                    new Point(lblMax-Round,0),
                    new Point(lblMax,Round),
                    //右下角
                    new Point(lblMax,this.Height-Round),
                    new Point(lblMax -Round,this.Height),
                    //左下角
                    new Point(X+Round ,this.Height),
                    new Point(X,this.Height-Round)
                };
                graphics.FillPolygon(brush, points);

                //绘制圆角
                graphics.FillEllipse(brush, X, 0, Round * 2, Round * 2);//左上圆角
                graphics.FillEllipse(brush, lblMax - Round * 2, 0, Round * 2, Round * 2);//右上圆角
                graphics.FillEllipse(brush, lblMax - Round * 2, this.Height - Round * 2, Round * 2, Round * 2);//右下圆角
                graphics.FillEllipse(brush, X, this.Height - Round * 2, Round * 2, Round * 2);//左下圆角

                //三角形
                Point[] points2 = new Point[]
                {
                    new Point(X,12),
                    new Point( 0,17),
                    new Point(X,22)
                };
                graphics.FillPolygon(brush, points2);
            }
        }

        //绘制箭头和圆角:气泡箭头在右侧
        private void DrawBubbleRight(Graphics graphics, Color c)
        {
            SolidBrush brush = new SolidBrush(c);//定义画刷
            int lblMax = X + M + lblWords.Width + M;

            if (rowsCount > 1)
            {
                //背景
                Point[] points = new Point[]
                {
                    //左上角
                    new Point(0,Round),
                    new Point(Round,0),
                    //右上角
                    new Point(this.Width-X-Round,0),
                    new Point(this.Width-X,Round),
                    //右下角
                    new Point(this.Width-X,this.Height-Round),
                    new Point(this.Width-X-Round,this.Height),
                    //左下角
                    new Point(Round ,this.Height),
                    new Point(0,this.Height-Round)
                };
                graphics.FillPolygon(brush, points);

                //绘制圆角
                graphics.FillEllipse(brush, 0, 0, Round * 2, Round * 2);//左上圆角
                graphics.FillEllipse(brush, this.Width - X - Round * 2, 0, Round * 2, Round * 2);//右上圆角
                graphics.FillEllipse(brush, this.Width - X - Round * 2, this.Height - Round * 2, Round * 2, Round * 2);//右下圆角
                graphics.FillEllipse(brush, 0, this.Height - Round * 2, Round * 2, Round * 2);//左下圆角

                //三角形
                Point[] points2 = new Point[]
                {
                    new Point(this.Width-X,12),
                    new Point(this.Width,17),
                    new Point(this.Width-X,22)
                };
                graphics.FillPolygon(brush, points2);
            }
            else
            {
                //背景
                Point[] points = new Point[]
                {
                    //左上角
                    new Point(this.Width-lblMax,Round),
                    new Point(this.Width-lblMax+Round,0),
                    //右上角
                    new Point(this.Width-X-Round,0),
                    new Point(this.Width-X,Round),
                    //右下角
                    new Point(this.Width-X,this.Height-Round),
                    new Point(this.Width-X-Round,this.Height),
                    //左下角
                    new Point(this.Width-lblMax+Round ,this.Height),
                    new Point(this.Width-lblMax,this.Height-Round)
                };
                graphics.FillPolygon(brush, points);

                //绘制圆角
                graphics.FillEllipse(brush, this.Width - lblMax, 0, Round * 2, Round * 2);//左上圆角
                graphics.FillEllipse(brush, this.Width - X - Round * 2, 0, Round * 2, Round * 2);//右上圆角
                graphics.FillEllipse(brush, this.Width - X - Round * 2, this.Height - Round * 2, Round * 2, Round * 2);//右下圆角
                graphics.FillEllipse(brush, this.Width - lblMax, this.Height - Round * 2, Round * 2, Round * 2);//左下圆角

                //三角形
                Point[] points2 = new Point[]
                {
                    new Point(this.Width-X,12),
                    new Point(this.Width,17),
                    new Point(this.Width-X,22)
                };
                graphics.FillPolygon(brush, points2);
            }
        }
        #endregion

        #region 事件
        //绘制气泡事件
        public void ucBubble_Paint(object sender, PaintEventArgs e)
        {
            Graphics graphics = e.Graphics;
            if (direction == "left")
                DrawBubbleLeft(graphics, gdiBackColor);
            else
                DrawBubbleRight(graphics, gdiBackColor);
        }

        //滚动事件
        public event Action BubbleMouseWheel;
        public void ucBubble_MouseWheel(object sender, MouseEventArgs e)
        {
            if (BubbleMouseWheel != null)
                BubbleMouseWheel();
        }

        //单击事件
        public event Action BubbleClick;
        public void ucBubble_MouseClick(object sender, MouseEventArgs e)
        {
            if (BubbleClick != null)
                BubbleClick();
        }

        //双击事件
        public event Action BubbleDoubleClick;
        public void ucBubble_DoubleClick(object sender, EventArgs e)
        {
            if (BubbleDoubleClick != null)
                BubbleDoubleClick();
        }

        //回车事件
        public event Action BubbleKeyPress;
        public void txtWords_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == 13)
            {
                e.Handled = true;

                if (BubbleKeyPress != null)
                    BubbleKeyPress();
            }
        }
        #endregion
    }
}

使用:

ucBubble uc1 = new ucBubble(250, Color.Brown, Color.Red, Color.Black, 12, "微软雅黑", "Hello World!", "left");
uc1.Location = new Point(20, 20);
this.Controls.Add(uc1);

ucBubble uc2 = new ucBubble(250, Color.Brown, Color.Green, Color.Black, 12, "微软雅黑", "Hello World!", "right");
uc2.Location = new Point(350, 40);
this.Controls.Add(uc2);

ucBubble uc3 = new ucBubble(150, Color.Brown, Color.Red, Color.Black, 12, "微软雅黑", "Hello World!Hello World!Hello World!Hello World!Hello World!", "left");
uc3.Location = new Point(20, 80);
this.Controls.Add(uc3);

ucBubble uc4 = new ucBubble(250, Color.Brown, Color.Green, Color.Black, 12, "微软雅黑", "Hello World!Hello World!Hello World!Hello World!", "right");
uc4.Location = new Point(350, 280);
this.Controls.Add(uc4);

效果:

 

一、数据采集层:多源人脸数据获取 该层负责从不同设备 / 渠道采集人脸原始数据,为后续模型训练与识别提供基础样本,核心功能包括: 1. 多设备适配采集 实时摄像头采集: 调用计算机内置摄像头(或外接 USB 摄像头),通过OpenCV的VideoCapture接口实时捕获视频流,支持手动触发 “拍照”(按指定快捷键如Space)或自动定时采集(如每 2 秒采集 1 张),采集时自动框选人脸区域(通过Haar级联分类器初步定位),确保样本聚焦人脸。 支持采集参数配置:可设置采集分辨率(如 640×480、1280×720)、图像格式(JPG/PNG)、单用户采集数量(如默认采集 20 张,确保样本多样性),采集过程中实时显示 “已采集数量 / 目标数量”,避免样本不足。 本地图像 / 视频导入: 支持批量导入本地人脸图像文件(支持 JPG、PNG、BMP 格式),自动过滤非图像文件;导入视频文件(MP4、AVI 格式)时,可按 “固定帧间隔”(如每 10 帧提取 1 张图像)或 “手动选择帧” 提取人脸样本,适用于无实时摄像头场景。 数据集对接: 支持接入公开人脸数据集(如 LFW、ORL),通过预设脚本自动读取数据集目录结构(按 “用户 ID - 样本图像” 分类),快速构建训练样本库,无需手动采集,降低系统开发与测试成本。 2. 采集过程辅助功能 人脸有效性校验:采集时通过OpenCV的Haar级联分类器(或MTCNN轻量级模型)实时检测图像中是否包含人脸,若未检测到人脸(如遮挡、侧脸角度过大),则弹窗提示 “未识别到人脸,请调整姿态”,避免无效样本存入。 样本标签管理:采集时需为每个样本绑定 “用户标签”(如姓名、ID 号),支持手动输入标签或从 Excel 名单批量导入标签(按 “标签 - 采集数量” 对应),采集完成后自动按 “标签 - 序号” 命名文件(如 “张三
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值