I'm building a "sequencer style" application. In part of it, I want to draw the waveform of an audio sample onto a row. However, the image doesn't fully appear until the cells it crosses are updated, either by selecting them, scrolling offscreen, or resizing. See gif below for examples:
I do the image draw in RowPrePaint
private void trackEditor_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
{
//I only prepaint PLAY SAMPLE rows, as they're the only ones that display waveforms
if (SequencerObjects[e.RowIndex].category == "PLAY SAMPLE") {
//PLAY SAMPLE cells get locked out of CellPainting(), so I paint them here first
//BEFORE drawing the waveform, otherwise the waveform gets covered
RowPrePainting = true;
e.PaintCells(e.RowBounds, DataGridViewPaintParts.All);
RowPrePainting = false;
//this check is done here instead of with the first if statement, so that the row cells are still painted even if the row has no values set.
if (!SequencerObjects[e.RowIndex].data_points.Any(x => x.value != null))
return;
//values used later in drawing the bitmap
int offsetportion = (trackEditor.Columns[3].Width - trackEditor.FirstDisplayedScrollingColumnHiddenWidth) + trackEditor.RowHeadersWidth + (trackEditor.Columns[0].Width * 3);
int columnindex = trackEditor.FirstDisplayedScrollingColumnIndex - FrozenColumnOffset + 1;
Sequencer_Object seqref = SequencerObjects[e.RowIndex];
SampleData samp = TCLE.ProjectSamples.FirstOrDefault(x => x.obj_name == seqref.obj_name);
//if samp is null, it won't have an audio file we can process into a wave
if (samp == null) {
return;
}
if (seqref.WaveBitmap == null) {
///Bunch of code here for math and creating the bitmap itself
///
}
//once the bitmap is created, now we can draw it
foreach (SeqDataPoint sdp in seqref.data_points.Where(x => x.value != null)) {
//if image location start would be offscreen, don't bother drawing it
if (sdp.beat > columnindex + trackEditor.DisplayedColumnCount(true) && sdp.beat + samp.beats < columnindex)
continue;
//math to offset drawing the wave horizontally based on where the active beats are
e.Graphics.DrawImage(seqref.WaveBitmap, ((sdp.beat - columnindex) * cellwidth) + offsetportion, e.RowBounds.Top + 2);
}
}
}
As I understand it, painting is triggered in this order: RowPrePaint
-> CellPainting
-> RowPostPaint
.
The image is drawn in RowPrePaint
. I have to draw it here since it spans multiple cells. I tried to draw it in CellPainting
at first but the graphics object available there is for 1 cellbounds only. I exclude all the cells for that row in CellPainting
. If those cells would be drawn again, they paint over the waveform image. If I draw the waveform in RowPostPaint
, then it paints over everything else (header, frozen columns, etc).
So part of my dilemma is that the waveform needs to be bottom-most (so it's drawn behind the header and frozen columns), yet still span across and on top of multiple cells.
The Question Is How can I trigger a repaint of the row's cells so that the waveform shows up, as it does when the cells are resized or selected?
If I programmatically resize the cells, that will re-fire the painting methods in a loop constantly. Same with select/deselect.