К сожалению, все элементы управления пользовательского интерфейса работают в одном потоке пользовательского интерфейса. Поэтому любой код, работающий в этом потоке, который потенциально может привести к ситуации зависания, должен быть закодирован с помощью некоторой логики тайм-аута.
DateTime startTime = DateTime.Now;
while(DateTime.Now.Subtract(startTime).TotalSeconds < 30)
{
//do something
}
В противном случае, как говорил ранее Орлангур, весь код обработчика событий должен выполняться в отдельных потоках. Тем не менее, вам все равно нужно следить за этими потоками, чтобы определить, слишком долго ли они работают, и закрыть их. Таким образом, вы могли бы также реализовать вышеописанную логику, так как она была бы намного менее трудоемкой и более удобной в обслуживании.