protected void OnTimerInThread()
{
// MAINTAINER NOTE: This method must renew the tickler. If it doesn't the animations will stop.
// If this listview has been destroyed, we can't do anything, so we return without
// renewing the tickler, effectively killing all animations on this renderer
if (this.ListView.IsDisposed)
return;
// If we're not in Detail view or our column has been removed from the list,
// we can't do anything at the moment, but we still renew the tickler because the view may change later.
if (this.ListView.View != System.Windows.Forms.View.Details || this.Column == null || this.Column.Index < 0) {
this.tickler.Change(1000, Timeout.Infinite);
return;
}
long elapsedMilliseconds = this.stopwatch.ElapsedMilliseconds;
int subItemIndex = this.Column.Index;
long nextCheckAt = elapsedMilliseconds + 1000; // wait at most one second before checking again
Rectangle updateRect = new Rectangle(); // what part of the view must be updated to draw the changed gifs?
// Run through all the subitems in the view for our column, and for each one that
// has an animation attached to it, see if the frame needs updating.
for (int i=0; i<this.ListView.GetItemCount(); i++) {
OLVListItem lvi = this.ListView.GetItem(i);
// Get the animation state from the subitem. If there isn't an animation state, skip this row.
OLVListSubItem lvsi = lvi.GetSubItem(subItemIndex);
AnimationState state = lvsi.AnimationState;
if (state == null || !state.IsValid)
continue;
// Has this frame of the animation expired?
if (elapsedMilliseconds >= state.currentFrameExpiresAt) {
state.AdvanceFrame(elapsedMilliseconds);
// Track the area of the view that needs to be redrawn to show the changed images
if (updateRect.IsEmpty)
updateRect = lvsi.Bounds;
else
updateRect = Rectangle.Union(updateRect, lvsi.Bounds);
}
// Remember the minimum time at which a frame is next due to change
nextCheckAt = Math.Min(nextCheckAt, state.currentFrameExpiresAt);
}
// Update the part of the listview where frames have changed
if (!updateRect.IsEmpty)
this.ListView.Invalidate(updateRect);
// Renew the tickler in time for the next frame change
this.tickler.Change(nextCheckAt - elapsedMilliseconds, Timeout.Infinite);
}