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

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

Winform仿制QQ微信聊天窗口气泡-LMLPHP

主要原理:一个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

05-15 06:03