因为公司业务原因,不能上传原始项目,这是简化版本。
临时设计的窗体和气泡样式,有需要可以重新设计。效果如下:

主要原理:一个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(0, 0);
= "panel1";
this.panel1.Size = new System.Drawing.Size(377, 404);
this.panel1.TabIndex = 0;
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(0, 406);
this.textBox1.Multiline = true;
= "textBox1";
this.textBox1.Size = new System.Drawing.Size(377, 65);
this.textBox1.TabIndex = 1;
//
// button1
//
this.button1.Location = new System.Drawing.Point(302, 477);
= "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 2;
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(380, 504);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.panel1);
= "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;
View Code
Form类后台代码:


/// <summary>
/// 当前消息气泡起始位置
/// </summary>
private int top = 0;
/// <summary>
/// 当前消息气泡高度
/// </summary>
private int height = 0;
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);
//计算高度
= top + height;
top = ;
height = item.HEIGHT;
//滚动条移动最上方,重新计算气泡在panel的位置
panel1.AutoScrollPosition = new Point(0, 0);
panel1.Controls.Add(item);
}
// <summary>
/// 更新界面,显示发送消息
/// </summary>
private void AddSendMessage(string content)
{
Item item = new Item();
item.messageType = WindowsFormsApplication2.Item.MessageType.send;
item.SetWeChatContent(content);
= top + height;
item.Left = 370 - item.WIDTH;
top = ;
height = item.HEIGHT;
panel1.AutoScrollPosition = new Point(0, 0);
panel1.Controls.Add(item);
}
View Code
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(20, 10);
this.panel1.MaximumSize = new System.Drawing.Size(370, 400);
= "panel1";
this.panel1.Padding = new System.Windows.Forms.Padding(10, 10, 5, 10);
this.panel1.Size = new System.Drawing.Size(26, 36);
this.panel1.TabIndex = 0;
//
// 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(5, 10);
this.lblContent.Margin = new System.Windows.Forms.Padding(0);
this.lblContent.MaximumSize = new System.Drawing.Size(280, 1000);
= "lblContent";
this.lblContent.Size = new System.Drawing.Size(16, 16);
this.lblContent.TabIndex = 5;
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);
= "Item";
this.Padding = new System.Windows.Forms.Padding(20, 10, 10, 5);
this.Size = new System.Drawing.Size(59, 54);
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;
View Code
Item 类后台代码:


/// <summary>
/// 本窗体总高度
/// </summary>
public int HEIGHT = 40;
/// <summary>
/// 本窗体总宽度
/// </summary>
public int WIDTH = 45;
/// <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[3];
point[0] = new Point(WIDTH - 5, 10);
point[1] = new Point(WIDTH - 15, 10);
point[2] = new Point(WIDTH - 15, 20);
e.Graphics.FillPolygon(brushes, point);
}
else
{
Color color = System.Drawing.Color.LightGray;
Brush brushes = new SolidBrush(color);
Point[] point = new Point[3];
point[0] = new Point(10, 10);
point[1] = new Point(20, 10);
point[2] = new Point(20, 20);
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
}
View Code
项目中的一些坑:
1. panel控件出现滚动条后,添加控件时需要重新计算相对位置,不然每个气泡间的间距会变大。比较简单的解决方法:每次添加控件前将滚动条移到最上方,添加完控件后再将滚动条移到最下方。
2. 设置双缓冲和不闪烁
3. 计算气泡位置和绘制小箭头,这个不难但是需要时间,不知道为什么按设计稿设置位置一直出错,对winform理解不够,wpf可能会更自由一点
Github:
https:///haibincoder/WinformBubble
时间会记录下一切。
















