{"id":406,"date":"2010-02-03T18:00:59","date_gmt":"2010-02-03T16:00:59","guid":{"rendered":"http:\/\/www.limilabs.com\/blog\/?p=406"},"modified":"2010-02-03T18:00:59","modified_gmt":"2010-02-03T16:00:59","slug":"background-processing-in-winforms","status":"publish","type":"post","link":"https:\/\/www.limilabs.com\/blog\/background-processing-in-winforms","title":{"rendered":"Background processing in WinForms"},"content":{"rendered":"<p>Many times developing windows applications, you&#8217;ll need to perform some operations in <strong>background<\/strong>.<\/p>\n<p>The problem you&#8217;ll face sooner or later is that those operations need to<strong> inform User Interface<\/strong> (UI) about their <strong>progress <\/strong>and <strong>completion<\/strong>.<\/p>\n<p>UI doesn&#8217;t like to be informed about anything from a different thread: you&#8217;ll get nasty &#8220;<strong>Cross-thread operation not valid<\/strong>&#8221; exception from WinForms controls, if you try.<\/p>\n<p>Let&#8217;s take a look at the sample Presenter code:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic void Start()\n{\n        TaskStatus taskStatus = this._backupService.CreateTask();\n        taskStatus.Completed += BackupFinished;\n        this._backupService.Start(taskStatus);\n}\n<\/pre>\n<p><strong>TaskStatus<\/strong> contains single event Completed.<br \/>\nWhat&#8217;ll do is that we&#8217;ll subscribe to this event to display some information on the View:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic void BackupFinished(object sender, EventArgs e)\n{\n        \/\/ If the operation is done on different thread,\n        \/\/ you'll get &quot;Cross-thread operation not valid&quot;\n        \/\/ exception from WinForms controls here.\n        this.View.ShowMessage(&quot;Finished!&quot;);\n}\n<\/pre>\n<p>So, what are the options:<\/p>\n<ul>\n<li>Use <em>Control.BeginInvoke<\/em> in View &#8211; makes your code unreadable<\/li>\n<li><a href=\"\/blog\/cross-thread-operations-with-postsharp\">Use Control.BeginInvoke with PostSharp<\/a> &#8211; you need PostSharp<\/li>\n<li><strong>SynchronizationContext<\/strong><\/li>\n<\/ul>\n<p>Lets examine the last concept as SynchronizationContext is not a well-know-class in the .NET world.<br \/>\nGenerally speaking this class is useful for synchronizing calls from worker thread to UI thread.<\/p>\n<p>It has a static <em>Current<\/em> property that gets the synchronization context for the current thread or null if there is no UI thread (e.g. in Console application)<\/p>\n<p>This is the <em>TaskStatus<\/em> class that utilizes <em>SynchronizationContext.Current<\/em> if it is not null:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class TaskStatus\n{\n    private readonly SynchronizationContext _context;\n\n    public event EventHandler Completed = delegate { };\n\n    public TaskStatus()\n    {\n        _context = SynchronizationContext.Current;\n    }\n\n    internal void OnCompleted()\n    {\n        Synchronize(x =&gt; this.Completed(this, EventArgs.Empty));\n    }\n\n    private void Synchronize(SendOrPostCallback callback)\n    {\n        if (_context != null)\n            _context.Post(callback, null);\n        else\n            callback(null);\n    }\n};\n<\/pre>\n<p>Now lets see some tests.<\/p>\n<p>First we&#8217;ll check if the event is executed:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;Test]\npublic void Completed_RaisesCompleted()\n{\n    using(SyncContextHelper.No())\n    {\n        bool wasFired = false;\n        TaskStatus status = new TaskStatus();\n        status.Completed += (sender, args) =&gt; { wasFired = true; };\n        status.OnCompleted();\n        Assert.IsTrue(wasFired);\n    }\n}\n<\/pre>\n<p>The following test shows that in <strong>WindowsForms application<\/strong>, although operation is executed on different thread, Completed<strong> event is routed<\/strong> back (using windows message queue) <strong>to the UI thread<\/strong>:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;Test]\npublic void Completed_WithSyncContext_IsExecutedOnSameThread()\n{\n    using (SyncContextHelper.WinForms())\n    {\n         int completedOnThread = -1;\n         int thisThread = Thread.CurrentThread.GetHashCode();\n\n         TaskStatus status = new TaskStatus();\n         status.Completed += (sender, args) =&gt;\n             {\n                 completedOnThread =\n                    Thread.CurrentThread.GetHashCode();\n             };\n\n         Scenario.ExecuteOnSeparateThread(status.OnCompleted);\n\n         \/\/ process messages send from background thread\n         \/\/ (like Completed event)\n         Application.DoEvents();\n\n         Assert.AreEqual(thisThread, completedOnThread);\n    }\n}\n<\/pre>\n<p>When there is no SynchronizationContext (<em>SynchronizationContext.Current<\/em> == <em>null<\/em>) <em>Completed<\/em> event is executed on the different thread:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;Test]\npublic void Completed_InMultiThreadedScenario_IsExecuedOnDifferentThread()\n{\n    using(SyncContextHelper.No())\n    {\n        int completedOnThread = -1;\n        int thisThread = Thread.CurrentThread.GetHashCode();\n\n        TaskStatus status = new TaskStatus();\n        status.Completed += (sender, args) =&gt;\n            {\n                completedOnThread =\n                    Thread.CurrentThread.GetHashCode();\n            };\n\n        Scenario.ExecuteOnSeparateThread(status.OnCompleted);\n\n        Assert.AreNotEqual(thisThread, completedOnThread);\n    }\n}\n<\/pre>\n<p>Finally unit test helper classes:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class SyncContextHelper : IDisposable\n{\n    private readonly SynchronizationContext _previous;\n\n    private SyncContextHelper(SynchronizationContext context)\n    {\n        _previous = SynchronizationContext.Current;\n        SynchronizationContext.SetSynchronizationContext(context);\n    }\n\n    public static SyncContextHelper WinForms()\n    {\n        return new SyncContextHelper(\n            new WindowsFormsSynchronizationContext());\n    }\n\n    public static SyncContextHelper No()\n    {\n        return new SyncContextHelper(null);\n    }\n\n    public void Dispose()\n    {\n        SynchronizationContext.SetSynchronizationContext(_previous);\n    }\n};\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Many times developing windows applications, you&#8217;ll need to perform some operations in background. The problem you&#8217;ll face sooner or later is that those operations need to inform User Interface (UI) about their progress and completion. UI doesn&#8217;t like to be informed about anything from a different thread: you&#8217;ll get nasty &#8220;Cross-thread operation not valid&#8221; exception [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[15,61],"class_list":["post-406","post","type-post","status-publish","format-standard","hentry","category-programming","tag-c","tag-winforms"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/406"}],"collection":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/comments?post=406"}],"version-history":[{"count":0,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/406\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/media?parent=406"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/categories?post=406"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/tags?post=406"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}