Trouble with "flickering" when updating a panel's children
I have a TableLayoutPanel
that contains rows of labels. This control lives on form with a gradient background. The labels are updated periodically, usually as a whole set. When they are updated, they update very slowly and it is very visible to the user.
I have tried using DoubleBuffered
on the form, but as expected, thi开发者_如何学Cs didn't help. Then I tried extending TableLayoutPanel
and setting its DoubleBuffered
property, but that seemed to make things worse. I've also tried to SuspendLayout()
on the table, but this didn't have any effect, either.
I've chosen this setup since the DataGridView
doesn't support transparency... I would really like to find an alternative to this setup, however.
Other questions seem related, but I haven't found one that solves the issue.
Any ideas on how to solve the update problem, or a possible alternative?
EDIT Since I'm dealing with a fixed number of datum to represent on this table, I'm looking into drawing the strings directly in the paint handler, rather than using the labels.
Are labels docked (meaning label.Dock = DockStyle.Fill
)?
How many code there are between each label.Text
change?
My application have exactly the same UI. Labels inside TableLayoutPanel inside TableLayoutPanel inside TableLayoutPanel. But without gradient background. Each panel and label has no margins and paddings. Each label is dock-filled. First we obtain all values for each label. And only after that we change all labels but in separate thread.
string[] texts = GetAllLabels();
ownerControl.Invoke(new Action(() =>
{
for (int i = 0; i < UpdatingControls.Count && i < texts.Count; i++)
{
UpdatingControls[i].Text = texts[i];
}
}));
This reduced (in our case) the blinking to the minimum.
The only way to eliminate the flicker was to use the paint handler of a custom control (such as a panel). The other methods I tried seemed to reduce flicker, but nothing took it away.
Based on what I saw, the labels themselves are each doing something similar, and when their data is changed, they invalidate themselves. There is no way to block this process, so the net effect is a scrolling/lagging update of each label to the next. This is amplified on a complex background (such as an image).
By using the paint handler and calculating bounds for each "row," I was able to get the same table look for my application, and its background remains transparent.
It's not novel code, but here it is in case it helps someone else:
private const int RowCount = 10;
private DataTable _table; // updated elsewhere
private void OnPaint(Object sender, PaintEventArgs e) {
if (! this.DesignMode) {
DrawTable(e.Graphics);
}
}
private void DrawTable(Graphics gfx) {
SolidBrush brush = new SolidBrush(this.ForeColor);
for (int rownum = 0; rownum < RowCount; rownum++) {
RectangleF rect = GetRowBounds(rownum);
DrawRowNumber(gfx, rect, rownum);
if (_table.Rows.Count > rownum) {
DataRow data = _table.Rows[rownum];
DrawName(gfx, rect, Convert.ToString(data["name"]));
DrawCount(gfx, rect, Convert.ToSingle(data["count"]));
}
}
}
private void DrawRowNumber(Graphics gfx, RectangleF bounds, int place) {
String str = String.Format("{0}.", (place + 1));
SolidBrush brush = new SolidBrush(this.ForeColor);
gfx.DrawString(str, this.Font, brush, bounds.Location);
}
private void DrawName(Graphics gfx, RectangleF bounds, String name) {
PointF loc = new PointF(80, bounds.Top);
SolidBrush brush = new SolidBrush(this.ForeColor);
gfx.DrawString(name, this.Font, brush, loc);
}
private void DrawCount(Graphics gfx, RectangleF bounds, float score) {
SolidBrush brush = new SolidBrush(this.ForeColor);
String str = score.ToString("N1");
SizeF size = gfx.MeasureString(str, this.Font);
float offset = bounds.Right - size.Width;
PointF loc = new PointF(offset, bounds.Top);
gfx.DrawString(str, this.Font, brush, loc);
}
private RectangleF GetRowBounds(int row) {
if ((row < 0) || (row >= RowCount)) {
return RectangleF.Empty;
}
Rectangle bounds = this.ClientRectangle;
float height = (float) bounds.Height / (float) RowCount;
PointF loc = new PointF(bounds.Left, height * row);
SizeF size = new SizeF(bounds.Width, height);
return new RectangleF(loc, size);
}
精彩评论