An update to my question.
As explained previously I needed this to be "universal". In other words the background thread shouldn't have any idea on how it will be used, or require a Control to be passed to the thread so Control.Invoke can be called.
The answer was to use the ISynchronizeInvoke interface directly. I found some sample code at StackOverflow and am using it like this (a different project, but same problem/solution):
private void HandleSyncCallback(Bitmap tile, int left, int top, CancelRenderArgs e)
{
Delegate[] handlers = _renderArgs.trCallBack.GetInvocationList();
foreach (var del in handlers)
{
ISynchronizeInvoke target = del.Target as ISynchronizeInvoke;
if ( (target != null))
{
if (target.InvokeRequired)
{
target.Invoke(del, new object[] {tile, left, top, e});
}
else
{
del.DynamicInvoke(new object[] {tile, left, top, e});
}
}
}
}
Works a treat.
Thanks,
Dave.