Rendering a generated table with a TableLayoutPanel that ends too long

My program is quite straightforward in concept - it allows the user to take points during bowling tournaments, as well as show players the results through the scoreboard.

There is the form of the account in which they enter the points, and the form of the scoreboard, which shows the ratings of the players, by dividing. The board runs in a different thread than the main program.

The scoreboard consists of a TableLayoutPanel, which I manipulate programmatically to present a table of the results that need to be shown. My problem is that it takes a lot of time to render a table (especially for a long list of players). The user experience of viewing a table rendering is poor.

I tested the rendering speed of text fields, labels and boxes to reduce the load; text fields won, so I changed the label labels to text fields ... but this is still not enough.

There is a lot to watch, but if anyone knows how I could further accelerate the rendering of the scoreboard, I would be all ears / eyes.

Here my process is broken.

The calling method (called every 15 seconds by a timer):

    private void switchBoard()
    {
        night = nights.GetNight(nightID);
        NightDay = night.Night_Date.ToString("ddd");
        //set the id of the division to show
        dtDivisions = scoreBoard.roll_display(dtDivisions);
        //get the row that is set to show
        DataRow[] drs = dtDivisions.Select("showing = 1");
        DataRow dr = drs[0];
        //update the title
        lbl_title.Top = 30;
        lbl_title.Text = (string)dr["title"] + " Qualifying - " + NightDay;
        lbl_title.Width = this.Width;
        lbl_title.TextAlign = ContentAlignment.MiddleCenter;
        //SET UP THE TABLE
        //get number of columns (games) for selected night
        int Cols = games.GetCountGamesForTourNightByDivision(tourID, nightID, scoreBoard.ShowDivision) + 3; //ACCOUNT FOR HEADER COLS, RANK AND TOTALS
        //get number of rows (players) for selected night
        int Rows = players.GetCountPlayersForTourNightByDivision(TourID, nightID, scoreBoard.ShowDivision) + 1; //ACCOUNT FOR HEADER ROWS
        //generate the table
        GenerateTable(Cols, Rows);
        //generate the headers
        GenerateHeaders(tourID, nightID);
        //fill in the scores
        GenerateScoreLabels(tourID, nightID, scoreBoard.ShowDivision);
    }

Creating a table:

    private void GenerateTable(int columnCount, int rowCount)
    {
        //Clear out the existing controls, we are generating a new table layout
        this.tblPnlScoreboard.Controls.Clear();

        //Clear out the existing row and column styles
        this.tblPnlScoreboard.ColumnStyles.Clear();
        this.tblPnlScoreboard.RowStyles.Clear();

        //setting up the row and column counts
        this.tblPnlScoreboard.ColumnCount = columnCount;
        this.tblPnlScoreboard.RowCount = rowCount;

        for (int x = 0; x < columnCount; x++)
        {
            //add a column
            if(x==0) //ranking column
            {
                this.tblPnlScoreboard.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute,30));
            }
            else if(x==1) //names
            {
                this.tblPnlScoreboard.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            }
            else if(x==columnCount-1) //totals
            {
                this.tblPnlScoreboard.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            }
            else //games
            {
                this.tblPnlScoreboard.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, (this.tblPnlScoreboard.Width - 130) / columnCount));
            }

            for (int y = 0; y < rowCount; y++)
            {
                //add rows.  Only do this once, when creating the first column
                if (x == 0)
                {
                    if(y==0)
                    {
                        this.tblPnlScoreboard.RowStyles.Add(new RowStyle(SizeType.Absolute, 50));
                    }
                    else
                    {
                        this.tblPnlScoreboard.RowStyles.Add(new RowStyle(SizeType.AutoSize));
                    }

                }
            }
        }
    }

Create Headers:

    private void GenerateHeaders(int TourID, int NightID)
    {
        //get the players to display
        DataTable dtPlayers = players.GetPlayersForTourNightByDivision(tourID, nightID, scoreBoard.ShowDivision);

        int Row = 1; //0 is the header row for Games and so on
        foreach (DataRow dr in dtPlayers.Rows)
        {
            //create the label
            Label lblPlayer = new Label();
            lblPlayer.Name = dr["ID"].ToString(); //name is the ID of the player
            lblPlayer.Text = dr["player_name"].ToString(); //the text is the name of the player
            lblPlayer.BackColor = Color.Transparent;
            lblPlayer.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
            lblPlayer.TextAlign = ContentAlignment.MiddleLeft;
            lblPlayer.AutoSize = true;
            lblPlayer.Height = tblPnlScoreboard.GetRowHeights()[Row];
            //add the label to the table
            this.tblPnlScoreboard.Controls.Add(lblPlayer, 1, Row);

            //create the Total label
            Label lblTotal = new Label();
            lblTotal.Name = "lbl_total"; //name is arbitrary in this context
            lblTotal.Text = dr["Total"].ToString(); //the text is the total
            lblTotal.BackColor = Color.Transparent;
            lblTotal.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
            lblTotal.TextAlign = ContentAlignment.MiddleLeft;
            lblTotal.AutoSize = true;
            lblTotal.Height = tblPnlScoreboard.GetRowHeights()[Row];
            //add the label to the table
            this.tblPnlScoreboard.Controls.Add(lblTotal, tblPnlScoreboard.ColumnCount, Row);

            //increment the row index
            Row++;
        }

        //totals column
        Label lblTotals = new Label();
        //lblTotals.Width = this.tblPnlScoreboard.GetColumnWidths()[this.tblPnlScoreboard.ColumnCount - 1];
        lblTotals.Height = tblPnlScoreboard.GetRowHeights()[0];
        lblTotals.Name = "lbl_total"; //name is the ID of the Game
        lblTotals.Text = "Totals"; //text is the display name of the Game
        lblTotals.BackColor = Color.Transparent;
        lblTotals.ForeColor = Color.White;
        lblTotals.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
        lblTotals.TextAlign = ContentAlignment.MiddleCenter;
        lblTotals.AutoSize = true;
        lblTotals.Anchor = (AnchorStyles.None);
        //add the label to the table
        this.tblPnlScoreboard.Controls.Add(lblTotals, this.tblPnlScoreboard.ColumnCount-1, 0);

        //get the games to display
        DataTable dtGames = games.GetGamesForTourNightByDivision(tourID, nightID, scoreBoard.ShowDivision);

        int Col = 2; //0 is the header column for rank, 1 is the header col for Players
        foreach (DataRow dr in dtGames.Rows)
        {
            //create the label
            Label lblGame = new Label();
            lblGame.Width = this.tblPnlScoreboard.GetColumnWidths()[Col];
            lblGame.Height = tblPnlScoreboard.GetRowHeights()[0];
            lblGame.Name = dr["ID"].ToString(); //name is the ID of the Game
            lblGame.Text = dr["disp_name"].ToString().Replace("Game ", ""); //text is the display name of the Game
            lblGame.BackColor = Color.Transparent;
            lblGame.ForeColor = Color.White;
            lblGame.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
            lblGame.TextAlign = ContentAlignment.MiddleCenter;
            lblGame.Anchor = (AnchorStyles.None);
            //add the label to the table
            this.tblPnlScoreboard.Controls.Add(lblGame, Col, 0);
            //increment the column index
            Col++;
        }
    }

Finally, creating ratings:

    private void GenerateScoreLabels(int TourID, int NightID, int DivID)
    {
        //get the id of the playergames record
        //expl: each player/game pair has a unique ID - these IDs will be used to update the scores
        Players players = new Players();
        DataTable dtScores = players.GetPlayerGamesIDsForTourNightByDivision(TourID, NightID, scoreBoard.ShowDivision);
        Divisions Divs = new Divisions();
        DataTable dtColors = Divs.GetDivisionScoreboardColors(DivID);

        foreach (DataRow dr in dtScores.Rows)
        {
            //find the coordinates in the table, where the textbox should be added
            int col = FindX((int)dr["fk_game_id"]);
            int row = FindY((int)dr["fk_player_id"]);

            if (col > 0 && row > 0)
            {                   
                TextBox txt_score = new TextBox();
                txt_score.Name = dr["ID"].ToString(); //name of the control is the player/game ID
                txt_score.Text = dr["score"].ToString(); //the text in the control is the score
                txt_score.ForeColor = Color.Black;
                txt_score.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
                txt_score.Width = this.tblPnlScoreboard.GetColumnWidths()[col];
                txt_score.Height = tblPnlScoreboard.GetRowHeights()[0];
                if(row % 2 == 0)
                {
                    txt_score.BackColor = Color.FromArgb((int)dtColors.Rows[0]["sb_even_row_color"]);
                }
                else
                {
                    txt_score.BackColor = Color.FromArgb((int)dtColors.Rows[0]["sb_odd_row_color"]);
                }
                txt_score.BorderStyle = BorderStyle.None;
                txt_score.TextAlign = HorizontalAlignment.Center;
                txt_score.Anchor = (AnchorStyles.Top);

                this.tblPnlScoreboard.Controls.Add(txt_score, col, row);

                //start the switchboard timer
                ttmr_switch.Enabled = true;
            }
        }
    }

In the CellPaint event for TableLayoutPanel, I have the following processes:

    private void tblPnlScoreboard_CellPaint(object sender, TableLayoutCellPaintEventArgs e)
    {
        Graphics g = e.Graphics;
        Rectangle r = e.CellBounds;
        SolidBrush sb = GetBrushFor(e.Row, e.Column, scoreBoard.ShowDivision);

        g.FillRectangle(sb, r);
        sb.Dispose();
    }

Choice of colors:

    private SolidBrush GetBrushFor(int row, int column, int DivID)
    {
        DataTable dt_colors = divisions.GetDivisionScoreboardColors(DivID);

        if (row == 0)
        {   //column headers
            SolidBrush brush = new SolidBrush(Color.FromArgb((int)dt_colors.Rows[0]["sb_column_header_color"]));
            return brush;
        }
        else
        {   
            if(row % 2 == 0) //even row
            {
                SolidBrush brush = new SolidBrush(Color.FromArgb((int)dt_colors.Rows[0]["sb_even_row_color"]));
                return brush;
            }
            else //odd row
            {
                SolidBrush brush = new SolidBrush(Color.FromArgb((int)dt_colors.Rows[0]["sb_odd_row_color"]));
                return brush;
            }
        }
    }
0
source share
2 answers

" ". " ". () / , , , , , 100 10 10 - , . :
1. Suspend/ResumeLayout, .
2. TableLayoutPanel, .
3. , .

, , , , . , .

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Tests
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ScoreBoardForm { WindowState = FormWindowState.Maximized });
        }
    }

    class ScoreBoardForm : Form
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            players = new List<Player>();
            for (int i = 0; i < 100; i++)
                players.Add(new Player { ID = i + 1, Name = "P" + (i + 1), Total = random.Next(1000) });
            games = new List<Game>();
            for (int i = 0; i < 10; i++)
                games.Add(new Game { ID = i + 1, Name = "G" + (i + 1) });

            scoreBoardTable = new ScoreBoardTable { Dock = DockStyle.Fill, Parent = this };
            scoreBoardTable.Bounds = this.DisplayRectangle;
            UpdateScoreBoard();
            scoreBoardTable.CellPaint += OnScoreBoardTableCellPaint;

            updateTimer = new Timer { Interval = 100 };
            updateTimer.Tick += (_sender, _e) => UpdateScoreBoard();
            updateTimer.Start();
        }
        private void OnScoreBoardTableCellPaint(object sender, TableLayoutCellPaintEventArgs e)
        {
            int playerIndex = e.Row - 1, gameIndex = e.Column - 2;
            if (playerIndex >= 0 && playerIndex < players.Count && gameIndex >= 0 && gameIndex < games.Count)
            {
                using (var br = new SolidBrush(GetBackColor(e.Row)))
                    e.Graphics.FillRectangle(br, e.CellBounds);
                var score = GetScore(players[playerIndex], games[gameIndex]);
                var sf = new StringFormat { Alignment = StringAlignment.Far, LineAlignment = StringAlignment.Center };
                e.Graphics.DrawString(score.ToString(), defaultCellFont, Brushes.Black, e.CellBounds, sf);
            }
        }
        private int GetScore(Player player, Game game)
        {
            return random.Next(10000);
        }
        class ScoreBoardTable : TableLayoutPanel
        {
            public ScoreBoardTable() { DoubleBuffered = AutoScroll = true; }
        }
        TableLayoutPanel scoreBoardTable;
        Timer updateTimer;
        List<Player> players;
        List<Game> games;
        Random random = new Random();
        class Player
        {
            public int ID;
            public string Name;
            public int Total;
        }
        class Game
        {
            public int ID;
            public string Name;
        }
        private void UpdateScoreBoard()
        {
            scoreBoardTable.SuspendLayout();
            GenerateTable(games.Count + 3, players.Count + 1);
            GenerateHeaderCells();
            // Custom cell paint is much faster, but requires a good data model.
            // If you uncomment the following line, make sure to get rid of CellPaint. 
            //GenerateScoreCells();
            scoreBoardTable.ResumeLayout(true);
        }
        private void GenerateTable(int columnCount, int rowCount)
        {
            scoreBoardTable.Controls.Clear();
            scoreBoardTable.ColumnStyles.Clear();
            scoreBoardTable.RowStyles.Clear();
            scoreBoardTable.ColumnCount = columnCount;
            scoreBoardTable.RowCount = rowCount;

            // Columns
            // Ranking
            scoreBoardTable.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 30));
            // Name 
            scoreBoardTable.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            // Games
            var percent = (columnCount - 3) / (float)columnCount;
            for (int col = 2; col < columnCount - 1; col++)
                scoreBoardTable.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, percent));
            // Totals
            scoreBoardTable.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));

            // Rows
            // Header
            scoreBoardTable.RowStyles.Add(new RowStyle(SizeType.Absolute, 50));
            // Players
            for (int row = 1; row < rowCount; row++)
                scoreBoardTable.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        }
        private void GenerateHeaderCells()
        {
            Color backColor = Color.DarkGray, foreColor;
            int row, col;
            // Header
            row = 0;
            foreColor = Color.White;
            col = 0;
            AddCell(row, col++, "rank", "", foreColor, backColor);
            AddCell(row, col++, "playerName", "Player", foreColor, backColor);
            foreach (var game in games)
                AddCell(row, col++, "gameName" + game.ID, game.Name, foreColor, backColor);
            AddCell(row, col, "totalColumn", "Totals", foreColor, backColor);
            // Rows
            foreColor = Color.Black;
            row++;
            foreach (var player in players)
            {
                backColor = GetBackColor(row);
                AddCell(row, 0, "playerRank_" + player.ID, "", foreColor, backColor, ContentAlignment.MiddleLeft);
                AddCell(row, 1, "playerName_" + player.ID, player.Name, foreColor, backColor, ContentAlignment.MiddleLeft);
                AddCell(row, scoreBoardTable.ColumnCount, "playerTotal_" + player.ID, player.Total.ToString(), foreColor, backColor, ContentAlignment.MiddleRight);
                row++;
            }
        }
        private void GenerateScoreCells()
        {
            var foreColor = Color.Black;
            int row = 1;
            foreach (var player in players)
            {
                var backColor = GetBackColor(row);
                int col = 2;
                foreach (var game in games)
                {
                    var score = GetScore(player, game);
                    AddCell(row, col, "score_" + player.ID + "_" + game.ID, score.ToString(), foreColor, backColor, ContentAlignment.MiddleRight, false);
                    col++;
                }
                row++;
            }
        }
        static readonly Font defaultCellFont = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
        private Label AddCell(int row, int col, string name, string text, Color foreColor, Color backColor, ContentAlignment textAlign = ContentAlignment.MiddleCenter, bool autoSize = true)
        {
            var label = new Label();
            label.Name = name;
            label.Text = text;
            label.BackColor = backColor;
            label.ForeColor = foreColor;
            label.Font = defaultCellFont;
            label.TextAlign = textAlign;
            label.AutoSize = autoSize;
            label.Margin = new Padding(0);
            label.Dock = DockStyle.Fill;
            scoreBoardTable.Controls.Add(label, col, row);
            return label;
        }
        static Color GetBackColor(int row)
        {
            if (row % 2 == 0) //even row
                return Color.Yellow;
            else //odd row
                return Color.LightGreen;
        }
    }
}

EDIT DataGridView ( , () ):

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Tests
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new ScoreBoardForm { WindowState = FormWindowState.Maximized });
        }
    }

    class ScoreBoardForm : Form
    {
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            players = new List<Player>();
            for (int i = 0; i < 1000; i++)
                players.Add(new Player { ID = i + 1, Name = "P" + (i + 1), Total = random.Next(1000) });
            games = new List<Game>();
            for (int i = 0; i < 10; i++)
                games.Add(new Game { ID = i + 1, Name = "G" + (i + 1) });

            InitScoreBoard();
            UpdateScoreBoard();

            updateTimer = new Timer { Interval = 100 };
            updateTimer.Tick += (_sender, _e) => UpdateScoreBoard();
            updateTimer.Start();
        }

        DataGridView scoreBoardTable;
        Timer updateTimer;
        List<Player> players;
        List<Game> games;
        Random random = new Random();
        class Player
        {
            public int ID;
            public string Name;
            public int Total;
        }
        class Game
        {
            public int ID;
            public string Name;
        }
        private int GetScore(Player player, Game game)
        {
            return random.Next(10000);
        }
        void InitScoreBoard()
        {
            scoreBoardTable = new DataGridView { Dock = DockStyle.Fill, Parent = this };
            scoreBoardTable.Font = new Font("Microsoft Sans Serif", 25, FontStyle.Bold);
            scoreBoardTable.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            scoreBoardTable.MultiSelect = false;
            scoreBoardTable.CellBorderStyle = DataGridViewCellBorderStyle.None;
            scoreBoardTable.BackgroundColor = Color.Honeydew;
            scoreBoardTable.ForeColor = Color.Black;
            scoreBoardTable.AllowUserToAddRows = scoreBoardTable.AllowUserToDeleteRows = scoreBoardTable.AllowUserToOrderColumns = scoreBoardTable.AllowUserToResizeRows = false;
            scoreBoardTable.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
            scoreBoardTable.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;
            scoreBoardTable.RowHeadersVisible = false;
            scoreBoardTable.EnableHeadersVisualStyles = false;
            var style = scoreBoardTable.DefaultCellStyle;
            style.SelectionForeColor = style.SelectionBackColor = Color.Empty;
            style = scoreBoardTable.ColumnHeadersDefaultCellStyle;
            style.SelectionForeColor = style.SelectionBackColor = Color.Empty;
            style.BackColor = Color.Navy;
            style.ForeColor = Color.White;
            style = scoreBoardTable.RowHeadersDefaultCellStyle;
            style.SelectionForeColor = style.SelectionBackColor = Color.Empty;
            style = scoreBoardTable.RowsDefaultCellStyle;
            style.SelectionForeColor = style.ForeColor = Color.Black;
            style.SelectionBackColor = style.BackColor = Color.Yellow;
            style = scoreBoardTable.AlternatingRowsDefaultCellStyle;
            style.SelectionForeColor = style.ForeColor = Color.Black;
            style.SelectionBackColor = style.BackColor = Color.LightGreen;
            scoreBoardTable.CellFormatting += OnScoreBoardCellFormatting;
        }
        private void UpdateScoreBoard()
        {
            scoreBoardTable.ColumnCount = 3 + games.Count;
            for (int c = 0; c < scoreBoardTable.ColumnCount; c++)
            {
                var col = scoreBoardTable.Columns[c];
                if (c == 0)
                {
                    col.Name = "Rank";
                    col.HeaderText = "";
                    col.AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
                    col.Width = 48;
                }
                else if (c == 1)
                {
                    col.Name = "Player";
                    col.HeaderText = "Player";
                    col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft;
                }
                else if (c == scoreBoardTable.ColumnCount - 1)
                {
                    col.Name = "Totals";
                    col.HeaderText = "Totals";
                    col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                    //col.AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
                }
                else
                {
                    var game = games[c - 2];
                    col.Name = "Game_" + game.ID;
                    col.HeaderText = game.Name;
                    col.DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
                    //col.AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
                }
            }
            scoreBoardTable.RowCount = players.Count;
            scoreBoardTable.AutoResizeColumnHeadersHeight();
            scoreBoardTable.Invalidate();
        }
        private void OnScoreBoardCellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            var player = players[e.RowIndex];
            int col = e.ColumnIndex;
            if (col == 0)
                e.Value = "";
            else if (col == 1)
                e.Value = player.Name;
            else if (col == scoreBoardTable.ColumnCount - 1)
                e.Value = player.Total.ToString();
            else
            {
                var game = games[col - 2];
                e.Value = GetScore(player, game).ToString();
            }
            e.FormattingApplied = true;
        }
    }
}
0

, CellPaint . , , CellPaint , , , .

- . HighCore, WPF, , , - .

0

All Articles