Иногда компонент Timer полезен и легко настраивается в WinForms, просто устанавливает его интервал и затем включает его, а затем убедитесь, что первое, что вы делаете в обработчике событий Tick, - это отключить себя.
Я думаю, что Timer запускает код в своем собственном потоке, поэтому вам может понадобиться выполнить BeginInvoke (вызываемый объектом WinForm [this]) для запуска вашего действия.
private WebBrowserDocumentCompletedEventHandler handler; //need to make it a class field for the handler below (anonymous delegates seem to capture state at point of definition, so they can't capture their own reference)
private string imageFilename;
private bool exit;
public void CaptureScreenshot(Uri address = null, string imageFilename = null, int msecDelay = 0, bool exit = false)
{
handler = (s, e) =>
{
webBrowser.DocumentCompleted -= handler; //must do first
this.imageFilename = imageFilename;
this.exit = exit;
timerScreenshot.Interval = (msecDelay > 0)? msecDelay : 1;
timerScreenshot.Enabled = true;
};
webBrowser.DocumentCompleted += handler;
Go(address); //if address == null, will use URL from UI
}
private void timerScreenshot_Tick(object sender, EventArgs e)
{
timerScreenshot.Enabled = false; //must do first
BeginInvoke((Action)(() => //Invoke at UI thread
{ //run in UI thread
BringToFront();
Bitmap bitmap = webBrowser.GetScreenshot();
if (imageFilename == null)
imageFilename = bitmap.ShowSaveFileDialog();
if (imageFilename != null)
{
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(imageFilename))); //create any parent directories needed
bitmap.Save(imageFilename);
}
bitmap.Dispose(); //release bitmap resources
if (exit)
Close(); //this should close the app, since this is the main form
}), null);
}
вы можете увидеть выше в действии в инструменте WebCapture ( http://gallery.clipflair.net/WebCapture , исходный код: http://ClipFlair.codeplex.com , см. раздел «Инструменты / Веб-камера»), который захватывает скриншоты с веб-сайтов. BTW, если вы хотите вызвать исполняемый файл из командной строки, убедитесь, что вы перешли в «Свойства проекта» и на вкладке «Безопасность» отключите защиту ClickOnce (иначе он не сможет получить доступ к командной строке)
Общий шаблон в DI состоит в том, что во время выполнения будет только одна реализация данной абстракции. Это просто делает жизнь намного проще, так как вам не нужно иметь дело с неоднозначностью, такой как та, которую вы описываете.
Однако иногда вам нужно изменить реализацию в зависимости от контекста, например, приведенный вами пример . Многие контейнеры DI предоставляют способы, с помощью которых вы можете указать квалифицирующий параметр, но это означает, что в конечном итоге вы будете жестко привязать свой код к конкретному контейнеру DI.
Намного лучшим решением было бы введение Abstract Factory ], которые могут предоставить то, что вам нужно. Что-то вроде
public interface ICarFactory
{
Car Create(IEngine engine);
}
Если вам нужно внедрить больше стратегий, возможно, шаблон проектирования Builder может подойти еще лучше.
В любом случае, Дело в том, что вместо регистрации множества разных автомобилей в контейнере вы должны вместо этого зарегистрировать одну реализацию ICarFactory.
В вашем клиентском коде вы должны использовать внедренный ICarFactory для создания экземпляра Car на основе определенного IEngine.
var car = factory.Create(engine);