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);
}