Используйте объект синхронизации, такой как Событие. Например, каждый фоновый поток имеет Событие, связанное с ним. Когда поток завершается, он сигнализирует о событии. Основной поток выполняет WaitHandle.WaitAll для набора событий и продолжается только тогда, когда сигнализируются все события.
Имейте в виду, что если существует вероятность того, что фоновые потоки прекратят работу в течение длительного времени, блокировка основного потока во время ожидания приведет к ухудшению работы пользователя. Так что, если это так, вы можете скрыть окно перед блокировкой. Кроме того, вы захотите проверить, как это повлияет на ваш делегат обратного вызова - если поток пользовательского интерфейса заблокирован в ожидании, сможет ли он обработать ваш делегат?
Не лучше ли не вызывать делегат, если поток прерывается из-за закрытия окна? Просто попросите основной поток сообщить фоновым потокам, почему они завершаются, и попросите их пропустить обратный вызов, если причиной является «закрытие окна». (Это предполагает, что вы общаетесь с потоками, как справедливо рекомендует Pax, а не просто вызываете Abort.)