Вместо того, чтобы закрывать / повторно вызывать окна, часто проще просто скрыть и показать.
Создайте окно всякий раз, когда вы создаете главное окно, и никогда не закрывайте его.Добавьте обработчик события onbeforeclose (или как-то еще, что был вызван снова ...), который отменяет закрытие, инициированное пользователем, и вместо этого скрывает окно.
Теперь вы уже используете поточно-ориентированный диспетчер для изменениясодержимое окна: просто добавьте одну строку в этот обработчик событий, чтобы отобразить окно (это ничего не изменит, если оно уже видно), и все готово!
Кстати, полезная вещь вэти сценарии являются пользовательским подклассом TextWriter.Вы можете сделать это следующим образом:
public abstract class AbstractTextWriter : TextWriter {
protected abstract void WriteString(string value);
public override Encoding Encoding { get { return Encoding.Unicode; } }
public override void Write(char[] buffer, int index, int count) {
WriteString(new string(buffer, index, count));
}
public override void Write(char value) {
WriteString(value.ToString(FormatProvider));
}
public override void Write(string value) { WriteString(value); }
//subclasses might override Flush, Dispose and Encoding
}
public class DelegateTextWriter : AbstractTextWriter {
readonly Action<string> OnWrite;
readonly Action OnClose;
static void NullOp() { }
public DelegateTextWriter(Action<string> onWrite, Action onClose = null) {
OnWrite = onWrite;
OnClose = onClose ?? NullOp;
}
protected override void WriteString(string value) { OnWrite(value); }
protected override void Dispose(bool disposing) {
OnClose(); base.Dispose(disposing);
}
}
Таким образом, вы можете вставить свою логику обновления формы в нечто вроде этого ...
var threadSafeLogWriter = new DelegateTextWriter(str => {
Action updateCmd = ()=>{
myControl...//append text to whatever control here
myControl.Show();
};
if(myControl.InvokeRequired) myControl.BeginInvoke(updateCmd);
else updateCmd();
});
... и использовать ее повсюду,потенциально даже делает Console.SetOut
, чтобы перехватить вывод до Console.Write
.Можно написать Write this TextWriter в нескольких потоках, поскольку реализация самосинхронизируется.
Вы можете проверить это следующим образом ...
Console.SetOut(threadSafeLogWriter);
Parallel.For(0,100, i=>{
threadSafeLogWriter.Write("Hello({0}) ", i);
Thread.Yield();
Console.WriteLine("World({0})!",i);
});
ЕслиВы делаете много регистрации маленьких сообщений, у вас может быть много вызовов BeginInvoke, и они медленные.В качестве оптимизации вы можете вместо этого поставить все сообщения журнала в ConcurrentQueue
или в другую синхронизированную структуру, и только в BeginInvoke, если очередь была пуста до того, как вы в нее включились.Таким образом, вы обычно делаете только один BeginInvoke между обновлениями пользовательского интерфейса;и когда пользовательский интерфейс приступает к выполнению своих задач, он очищает флаг, а затем добавляет весь помещенный в очередь текст сразу.Однако, поскольку все потоки, ведущие журналирование, просто сбрасывают свои сообщения в том порядке, в каком они поступают в регистратор, для удобства чтения может быть очень плохо иметь множество небольших операторов журнала;Лучше всего сразу записать как можно большую строку, чтобы она не прерывалась сообщениями других потоков;и если вы сделаете это, то у вас не будет много BeginInvoke'а, так или иначе.будет меньше проблем.