因为公司业务原因,不能上传原始项目,这是简化版本。

临时设计的窗体和气泡样式,有需要可以重新设计。效果如下:

主要原理:一个TextBlock + 一个三角形

项目结构:

-- Form1 窗体类

-- Item 控件类(气泡)

Form1前端代码:

#region Windows 窗体设计器生成的代码

        /// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.textBox1 = new System.Windows.Forms.TextBox();
this.button1 = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// panel1
//
this.panel1.AutoScroll = true;
this.panel1.Location = new System.Drawing.Point(, );
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(, );
this.panel1.TabIndex = ; //
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(, );
this.textBox1.Multiline = true;
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(, );
this.textBox1.TabIndex = ; //
// button1
//
this.button1.Location = new System.Drawing.Point(, );
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(, );
this.button1.TabIndex = ;
this.button1.Text = "Send";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button1_Click); //
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(, );
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.panel1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
this.PerformLayout(); } #endregion private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Button button1;

Form类后台代码:

/// <summary>
/// 当前消息气泡起始位置
/// </summary>
private int top = ; /// <summary>
/// 当前消息气泡高度
/// </summary>
private int height = ; private void button1_Click(object sender, EventArgs e)
{
AddSendMessage(textBox1.Text);
AddReceiveMessage(textBox1.Text);
} /// <summary>
/// 显示接收消息
/// </summary>
/// <param name="model"></param>
private void AddReceiveMessage(string content)
{
Item item = new Item();
item.messageType = WindowsFormsApplication2.Item.MessageType.receive;
item.SetWeChatContent(content); //计算高度
item.Top = top + height;
top = item.Top;
height = item.HEIGHT; //滚动条移动最上方,重新计算气泡在panel的位置
panel1.AutoScrollPosition = new Point(, );
panel1.Controls.Add(item);
} // <summary>
/// 更新界面,显示发送消息
/// </summary>
private void AddSendMessage(string content)
{
Item item = new Item();
item.messageType = WindowsFormsApplication2.Item.MessageType.send;
item.SetWeChatContent(content);
item.Top = top + height;
item.Left = - item.WIDTH; top = item.Top;
height = item.HEIGHT;
panel1.AutoScrollPosition = new Point(, );
panel1.Controls.Add(item);
}

Item类前端代码:

#region 组件设计器生成的代码

        /// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{ this.panel1 = new System.Windows.Forms.Panel();
this.lblContent = new System.Windows.Forms.Label();
this.panel1.SuspendLayout();
this.SuspendLayout();
//
// panel1
// this.panel1.AutoSize = true;
this.panel1.BackColor = System.Drawing.Color.LightGray;
this.panel1.Controls.Add(this.lblContent);
this.panel1.Location = new System.Drawing.Point(, );
this.panel1.MaximumSize = new System.Drawing.Size(, );
this.panel1.Name = "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(, , , );
this.panel1.Size = new System.Drawing.Size(, );
this.panel1.TabIndex = ;
//
// lblContent
//
this.lblContent.AutoSize = true;
this.lblContent.Font = new System.Drawing.Font("宋体", 16F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Pixel);
this.lblContent.ForeColor = System.Drawing.Color.White;
this.lblContent.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.lblContent.Location = new System.Drawing.Point(, );
this.lblContent.Margin = new System.Windows.Forms.Padding();
this.lblContent.MaximumSize = new System.Drawing.Size(, );
this.lblContent.Name = "lblContent";
this.lblContent.Size = new System.Drawing.Size(, );
this.lblContent.TabIndex = ;
this.lblContent.Text = " ";
this.lblContent.Visible = false; //
// Item
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.AutoSize = true;
this.Controls.Add(this.panel1);
this.Name = "Item"; this.Padding = new System.Windows.Forms.Padding(, , , );
this.Size = new System.Drawing.Size(, );
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout(); } #endregion private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label lblContent;

Item 类后台代码:

/// <summary>
/// 本窗体总高度
/// </summary>
public int HEIGHT = ;
/// <summary>
/// 本窗体总宽度
/// </summary>
public int WIDTH = ;
/// <summary>
/// 消息类型
/// </summary>
public MessageType messageType; public Item()
{
///设置控件样式
SetStyle(
ControlStyles.AllPaintingInWmPaint | //不闪烁
ControlStyles.OptimizedDoubleBuffer //支持双缓存
, true);
InitializeComponent();
this.Paint += Item_Paint;
} #region 界面重绘 /// <summary>
/// 绘制气泡左上角小箭头
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Item_Paint(object sender, PaintEventArgs e)
{
//自己发送的消息箭头在右上角
if (messageType == MessageType.send)
{ Color color = System.Drawing.Color.LightGray;
panel1.BackColor = color;
Brush brushes = new SolidBrush(color);
Point[] point = new Point[];
point[] = new Point(WIDTH - , );
point[] = new Point(WIDTH - , );
point[] = new Point(WIDTH - , );
e.Graphics.FillPolygon(brushes, point);
}
else
{ Color color = System.Drawing.Color.LightGray;
Brush brushes = new SolidBrush(color);
Point[] point = new Point[];
point[] = new Point(, );
point[] = new Point(, );
point[] = new Point(, );
e.Graphics.FillPolygon(brushes, point);
}
}
#endregion #region 功能操作 /// <summary>
/// 设置气泡内容
/// </summary>
/// <param name="type">消息类型</param>
/// <param name="content">消息内容</param>
public void SetWeChatContent(string content)
{ lblContent.Text = content;
lblContent.Visible = true;
HEIGHT += lblContent.Height;
WIDTH += lblContent.Width;
} #endregion /// <summary>
/// 内部类
/// </summary> class MessageItem
{
public string RESPATH { get; set; }
public string RESTYPE { get; set; }
}
/// <summary>
/// 消息类型
/// </summary>
public enum MessageType
{
send,
receive
}

项目中的一些坑:

1. panel控件出现滚动条后,添加控件时需要重新计算相对位置,不然每个气泡间的间距会变大。比较简单的解决方法:每次添加控件前将滚动条移到最上方,添加完控件后再将滚动条移到最下方。

2. 设置双缓冲和不闪烁

3. 计算气泡位置和绘制小箭头,这个不难但是需要时间,不知道为什么按设计稿设置位置一直出错,对winform理解不够,wpf可能会更自由一点

Github:

https://github.com/haibincoder/WinformBubble

Winform仿制QQ微信聊天窗口气泡的更多相关文章

  1. C#绘制三角形并填充,使用winform实现qq聊天气泡

    首先是需求,需要制作一个聊天气泡, 但是winform中有没有类似Android的.9图,只有自己设计图形拼接气泡. 第一种是绘制空心三角形,第二种是绘制三角形区域,可以指定RGB颜色. privat ...

  2. winform实现QQ聊天气泡200行代码

    c# winform实现QQ聊天气泡界面,原理非常简单,通过webKitBrowser(第三方浏览器控件,因为自带的兼容性差)加载html代码实现,聊天界面是一个纯HTML的代码,与QQ的聊天界面可以 ...

  3. Winform调用QQ发信息并且开机启动 (开源)

    前言 公司CS系统需要加入启动qq从winform调用qq聊天窗口的功能,前提是需要将聊天者的QQ号码作为参数传递到函数中,一直没有搞过,正好很感兴趣,就折腾,Winform调用qq,我想肯定是需要一 ...

  4. 访问量分类统计(QQ,微信,微博,网页,网站APP,其他)

    刚准备敲键盘,突然想起今天已经星期五了,有点小兴奋,一周又这么愉快的结束,又可以休息了,等等..我好像是来写Java博客的,怎么变成了写日记,好吧,言归正传. 不知道大家有没有遇到过这样的需求:统计一 ...

  5. 拾人牙慧篇之———QQ微信的第三方登录实现

    一.写在前面 关于qq微信登录的原理之流我就不一一赘述了,对应的官网都有,在这里主要是展示我是怎么实现出来的,看了好几个博客,有的是直接复制官网的,有的不知道为什么实现不了.我只能保证我的这个是我实现 ...

  6. Oauth2.0 QQ&微信&微博实现第三方登陆

    一.写在前面 目前对于大多数的App或Web网站都支持有第三方登陆这个功能,用户可使用 QQ/ 微信/ 微博 帐号快速登录你的网站,降低注册门槛,为你的网站带来海量新用户.最近在新项目上刚好用到了,在 ...

  7. HTML5实现微信聊天气泡效果

    最近做一个HybridApp,前端有一个群聊的功能,于是就想模仿微信的聊天界面,先看效果图: HTML代码: <!DOCTYPE html> <html lang="en& ...

  8. Ubuntu16.04或18.04上安装QQ微信迅雷

    0. 写在前面 没办法,公司的电脑是Windows的,windows下面开发实在太恶心人,并且开发中需要编译golang和C++的程序,于是开始了Linux的折腾之路. 如果你只是想用Linux环境开 ...

  9. 制作QQ微信支付宝三合一收款码

    转载:http://blog.mambaxin.com/article/56 发现很多博客都带了打赏功能,虽说打赏的人可能很少,但始终是一份心意,能让博主知道自己写的文章有用,能够帮助到人.所以,我也 ...

随机推荐

  1. golang ---image

    package main import ( "image" "image/color" "image/gif" "os" ...

  2. MySQL -- Fast Index Creation

    1.fast index creation简介 MySQL5.5之后,对innodb表创建或删除辅助索引的效率提升了很多,即增加了新的功能fast index creation.因为MySQL5.5之 ...

  3. CListCtrl的Report风格自绘

    原文链接: http://jingyan.baidu.com/article/5bbb5a1b38af1113eaa17910.html CListCtrl是MFC中运用最广泛的控件之一,很多软件都有 ...

  4. Spring boot注解(annotation)含义详解

    Spring boot注解(annotation)含义详解 @Service用于标注业务层组件@Controller用于标注控制层组件(如struts中的action)@Repository用于标注数 ...

  5. nodejs 遍历文件夹下所有的图片改名为中文

    安装依赖 $ npm init -y && npm i fs-extra globby request -S main.js const fs = require('node-fs-e ...

  6. JSON.toJSONString中序列化空字符串遇到的坑

    前言 最近在做系统Bug修复时遇到了一个问题,调用其他服务时传递的参数和自己预先的不一致,例如Map中有10条记录,然后使用JSON.toJSONString 包装后进行网络传递,但是通过调试发现接收 ...

  7. SpringBoot+mybatis实现多数据源支持

    什么是多数据源支持? 简单的说,就是一个项目里,同时可以访问多个不同的数据库. 实现原理 单个数据源在配置时会绑定一套mybatis配置,多个数据源时,不同的数据源绑定不同的mybatis配置就可以了 ...

  8. xocodebulid 自动化打包 解决提示 ld: library not found for -lPods 问题

    如果你的项目用到cocopod 第三方库.使用xcodebulid 估计会出现 ld: library not found for -lPods 以下 是我的解决办法 xcodebuild -work ...

  9. 使用vs的查找功能,简单大概的统计vs中的代码行数

    VS强大的查找功能,可以使用正则表达式来进行查找,这里统计代码行数的原理就是: 在所有指定文件中进行搜索,统计匹配的文本行数. 但是匹配的行需要满足:非注释.非空等特殊非代码行. 使用Ctrl+Shi ...

  10. ios总结2018

      1.为什么说Objective-C是一门动态的语言? 1.object-c类的类型和数据变量的类型都是在运行是确定的,而不是在编译时确定.例如:多态特性,我们可以使用父类指针来指向子类对象,并且可 ...