Pagination in TableLayoutPanel when Adding Controls

Denis Morgan 20 Reputation points
2023-10-20T05:31:53.91+00:00

I am creating a chat application using C# window form. But I have a challenge on how to load chat messages using pagination like what skype does that is assuming a chat has 1000 chats I need to first load the last like 50 messages on the TableLayoutPanel. Then when the user scroll up the TableLayoutPanel the other chat (950)get loaded based on the screen size to improve the performance. Here is my implementation of my chat message app. Am using .net 4.5.2

 TableLayoutPanel tableLayoutPanel1 = new TableLayoutPanel();
        WsChatConnection chatConnection;
        string sender_id;
        Mdi mdi;
        ChatServiceHandler http;
        UserChatRequestModel userChatRequestModel;
        public frmChatWindow(Mdi mdi)
        {
            InitializeComponent();
            this.mdi = mdi;

            http = new ChatServiceHandler();
            userChatRequestModel = new UserChatRequestModel();

         
            usersData = http.getAllChatUsers();
            basex = bubblePanel.Location.X;//bubble panel house TableLayoutPanel
            basey = 0
            tableLayoutPanel1.Dock = DockStyle.Fill;

            this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50F));
            tableLayoutPanel1.HorizontalScroll.Maximum = 0;
            tableLayoutPanel1.AutoScroll = false;
            tableLayoutPanel1.VerticalScroll.Visible = false;
            tableLayoutPanel1.AutoScroll = true;
            bubblePanel.Controls.Add(tableLayoutPanel1);
           


        }
		
		   void loadUserChats()
        {
            IList<UserConversation> conversations = http.getUsersPreviousChats(userChatRequestModel);//this returns data in milliseconds.
            if (conversations.Count > 0)
            {
                
                IList<Conversation> chats = conversations[0].chat;
           
                int tally = 0;
                tableLayoutPanel1.SuspendLayout();
				int row=0;
                foreach (Conversation chat in chats)//this loop is taking forever and freezes as the messages are many. I need a way to load conversation on user scroll
                {
                   CustomControl control=new CustomControl();
				   control.LabelText=chat.message;
                   tableLayoutPanel1.Controls.Add(control, 0, row);
				   row++;
                    tally++;
                }
                tableLayoutPanel1.ResumeLayout();
                tableLayoutPanel1.VerticalScroll.Value = tableLayoutPanel1.VerticalScroll.Maximum;
                tableLayoutPanel1.ScrollControlIntoView(messageBottom);
              
                panel2.Visible = true;
            }

        }
.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,452 questions
C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,364 questions
{count} votes

1 answer

Sort by: Most helpful
  1. KOZ6.0 5,050 Reputation points
    2023-10-21T04:01:53.1666667+00:00

    I tried making it using DataGridView. If there are 1000 items, it will take some time to adjust the height, so some kind of ingenuity will be necessary. I fixed it a little.

    using System;
    using System.ComponentModel;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Reflection;
    using System.Windows.Forms;
    
    public partial class Form2 : Form
    {
        private const TextFormatFlags textFormatFlags =
                              TextFormatFlags.TextBoxControl |
                              TextFormatFlags.WordBreak;
        private const int ChatIndent = 48;
        private const int borderSize = 1;
        private static Padding cellPadding = new Padding(8);
        private static Padding bubblePadding = new Padding(4);
        private const int bubbleRadius = 8;
        private readonly BindingList<Comment> comments
                                = new BindingList<Comment>();
        class Comment
        {
            public string Text { get; set; }
            public bool IsMine { get; set; }
    
            [Browsable(false)] public Size TextSize;
            [Browsable(false)] public int ProposeWidth;
            [Browsable(false)] public Font ProposeFont;
    
            public Comment(string text, bool isMine) {
                Text = text;
                IsMine = isMine;
            }
    
            public bool IsChanged(int width, Font font) {
                if (ProposeWidth != width) {
                    Save(width, font);
                    return true;
                }
                if (ProposeFont == null || ProposeFont != font) {
                    Save(width, font);
                    return true;
                }
                return false;
            }
    
            private void Save(int width, Font font) {
                ProposeWidth = width;
                ProposeFont = font;
            }
        }
    
        public Form2() {
            InitializeComponent();
        }
    
        private void Form2_Load(object sender, EventArgs e) {
            var pi = typeof(DataGridView).GetProperty("DoubleBuffered",
                            BindingFlags.NonPublic | BindingFlags.Instance);
            if (pi != null) {
                pi.SetValue(dataGridView1, true);
            }
            int index = 1;
            for (int i = 0; i < 250; i++) {
                comments.Add(new Comment($"{index++}: I am creating a chat application using C# window form. But I have a challenge on how to load chat messages using pagination like what skype does that is assuming a chat has 1000 chats I need to first load the last like 50 messages on the TableLayoutPanel.", false));
                comments.Add(new Comment($"{index++}: Wait, I was expecting at a few dozen controls. Creating 1000 controls causes performance issues.", true));
                comments.Add(new Comment($"{index++}: Sorry, I should have suggested another method.", true));
                comments.Add(new Comment($"{index++}: Hmm, It is trouble. DataGridView and ListView are suitable for displaying from the top, but not for displaying from the bottom.", true));
            }
            dataGridView1.DataSource = comments;
            dataGridView1.ScrollBars = ScrollBars.Vertical;
            dataGridView1.AllowUserToAddRows = false;
            dataGridView1.RowHeadersVisible = false;
            dataGridView1.ColumnHeadersVisible = false;
            var column0 = dataGridView1.Columns[0];
            if (column0 != null) {
                column0.Resizable = DataGridViewTriState.False;
                column0.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                column0.ReadOnly = true;
            }
            dataGridView1.Columns[1].Visible = false;
        }
    
        private void Form2_Shown(object sender, EventArgs e) {
            AdjustHeight(dataGridView1.RowCount - 50);
            dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
        }
    
        private int ProposeWidth {
            get {
                return dataGridView1.ClientSize.Width -
                       SystemInformation.VerticalScrollBarWidth -
                       ChatIndent - 
                       cellPadding.Horizontal;
            }
        }
    
        private Font ProposeFont {
            get {
                return
                    dataGridView1.Columns[0].DefaultCellStyle.Font ??
                    dataGridView1.DefaultCellStyle.Font ??
                    dataGridView1.Font;
            }
        }
    
        private void DataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) {
            if (e.RowIndex > -1 && e.ColumnIndex == 0) {
                DrawCell(e, comments[e.RowIndex]);
                e.Handled = true;
            }
        }
    
        private void DataGridView1_SizeChanged(object sender, EventArgs e) {
            AdjustDisplay();
        }
    
        private void DataGridView1_Scroll(object sender, ScrollEventArgs e) {
            AdjustDisplay();
        }
    
        private void AdjustDisplay() {
            int start = dataGridView1.FirstDisplayedScrollingRowIndex;
            int end = dataGridView1.Rows.GetLastRow(DataGridViewElementStates.Displayed);
            AdjustHeight(start - 10, end + 50);
        }
    
        private void AdjustHeight(int start) {
            AdjustHeight(start, dataGridView1.RowCount - 1);
        }
    
        private void AdjustHeight(int start, int end) {
            start = Math.Max(start, 0);
            end = Math.Min(end, dataGridView1.RowCount - 1);
            using (var g = dataGridView1.CreateGraphics()) {
                for (int rowIndex = start; rowIndex <= end; rowIndex++) {
                    var comment = comments[rowIndex];
                    var row = dataGridView1.Rows[rowIndex];
                    if (comment.IsChanged(ProposeWidth, ProposeFont)) {
                        comment.TextSize =
                                TextRenderer.MeasureText(
                                    g, comment.Text, ProposeFont,
                                    new Size(ProposeWidth, 1), textFormatFlags);
                    }
                    int height = 
                                comment.TextSize.Height +
                                cellPadding.Vertical +
                                bubblePadding.Vertical +
                                borderSize * 2;
                    if (row.Height != height) {
                        row.Height = height;
                    }
                }
            }
        }
    
        private void DrawCell(DataGridViewCellPaintingEventArgs e, Comment comment) {
            var text = comment.Text;
            var isMine = comment.IsMine;
            using (var brush = new SolidBrush(dataGridView1.BackgroundColor)) {
                e.Graphics.FillRectangle(brush, e.CellBounds);
            }
            var textSize = comment.TextSize;
            var bubbleSize = new Size(textSize.Width + bubblePadding.Horizontal,
                                      textSize.Height + bubblePadding.Vertical);
            Point bubbleLocation;
            Color bubbleColor;
            Color textColor;
            if (isMine) {
                bubbleLocation = new Point(e.CellBounds.Right - bubbleSize.Width,
                                           e.CellBounds.Top + cellPadding.Top);
                textColor = Color.White;
                bubbleColor = Color.DodgerBlue;
            } else {
                bubbleLocation = new Point(e.CellBounds.Left,
                                           e.CellBounds.Top + cellPadding.Top);
                textColor = Color.Black;
                bubbleColor = SystemColors.Window;
            }
            Rectangle bubbleArea = new Rectangle(bubbleLocation, bubbleSize);
            FillRoundRectangle(e.Graphics, bubbleArea, bubbleRadius, bubbleColor);
    
            Point textLocation = new Point(bubbleLocation.X + bubblePadding.Left,
                                           bubbleLocation.Y + bubblePadding.Top);
            Rectangle textArea = new Rectangle(textLocation, textSize);
            TextRenderer.DrawText(
                        e.Graphics, text, ProposeFont,
                        textArea, textColor, textFormatFlags);
        }
    
        private static void FillRoundRectangle(Graphics g, Rectangle bounds, int radius, Color color) {
            using (var path = GetRoundedRectangle(bounds, radius))
            using (var brush = new SolidBrush(color)) {
                g.FillPath(brush, path);
            }
        }
    
        private static GraphicsPath GetRoundedRectangle(Rectangle baseRect, int radius) {
            GraphicsPath path = new GraphicsPath();
            path.AddArc(baseRect.X, baseRect.Y, radius * 2, radius * 2, 180, 90);
            path.AddArc(baseRect.Right - radius * 2, baseRect.Y, radius * 2, radius * 2, 270, 90);
            path.AddArc(baseRect.Right - radius * 2, baseRect.Bottom - radius * 2, radius * 2, radius * 2, 0, 90);
            path.AddArc(baseRect.X, baseRect.Bottom - radius * 2, radius * 2, radius * 2, 90, 90);
            path.CloseFigure();
            return path;
        }
    }
    

    enter image description here