Winform仿制QQ微信聊天窗口氣泡


因為公司業務原因,不能上傳原始項目,這是簡化版本。

臨時設計的窗體和氣泡樣式,有需要可以重新設計。效果如下:

主要原理:一個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);
            this.panel1.Name = "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;
            this.textBox1.Name = "textBox1";
            this.textBox1.Size = new System.Drawing.Size(377, 65);
            this.textBox1.TabIndex = 1;

            // 
            // button1
            // 
            this.button1.Location = new System.Drawing.Point(302, 477);
            this.button1.Name = "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);
            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;
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);

//計算高度
            item.Top = top + height;
            top = item.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);
            item.Top = top + height;
            item.Left = 370 - item.WIDTH;

top = item.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);
            this.panel1.Name = "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);
            this.lblContent.Name = "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);
            this.Name = "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://github.com/haibincoder/WinformBubble


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM