我的计划在概念上非常直接 - 它允许用户在保龄球锦标赛期间获得分数,以及通过记分牌向玩家展示分数。
有一个分数表单,用于输入分数,以及一个记分牌表单,按分区显示给玩家的分数。记分板在与主程序不同的线程中运行。
记分板由TableLayoutPanel组成,我以编程方式操作以表示要显示的分数表。我的问题是,要渲染表需要很长时间(特别是对于很长的玩家列表)。观看桌面呈现的用户体验也是不可取的。
我测试了渲染文本框,标签和图片框的速度以减轻负载;赢得了文本框,所以我将得分标签更改为文本框...但仍然不够。
要浏览很多内容,但如果有人知道如何进一步加快记分板的渲染速度,那么我将全力以赴。
这是我的流程细分。
调用方法(由计时器每15秒调用一次):
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"] + "'s 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);
}
生成表格:
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));
}
}
}
}
}
生成标题:
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++;
}
}
最后,生成分数:
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;
}
}
}
在TableLayoutPanel的CellPaint事件中,我有以下过程:
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();
}
颜色选择:
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 :(得分:0)
嗯,CellPaint事件是罪魁祸首。我能够通过否定CellPaint事件来实现我想要的,而是操纵面板中的控件,以便具有正确的背景颜色和大小,以便它们填充网格。
感谢您的所有评论 - 他们非常乐于助人。根据HighCore的评论,我现在将研究WPF,我可能会得到一些更加光滑的东西。
答案 1 :(得分:0)
有些人建议你使用“适当的技术”。我宁愿说“正确使用技术”。即使是这种奇怪的(对不起)设计/实现选择也可以更快地工作,如下面的代码所示,正如您所看到的那样,处理重建包含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;
}
}
}
编辑以下是使用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;
}
}
}