{"id":1468,"date":"2014-11-05T20:57:10","date_gmt":"2014-11-05T19:57:10","guid":{"rendered":"http:\/\/doena-journal.net\/en\/?p=1468"},"modified":"2021-09-27T15:44:13","modified_gmt":"2021-09-27T13:44:13","slug":"wpf-mvvm-responsive-ui","status":"publish","type":"post","link":"http:\/\/doena-journal.net\/en\/1468\/wpf-mvvm-responsive-ui","title":{"rendered":"WPF: MVVM &amp; Responsive UI"},"content":{"rendered":"<p><a href=\"http:\/\/doena-journal.net\/2312\/wpf-mvvm-responsive-ui\">Deutsche Version<\/a><\/p>\n<p>A few days ago I wrote about my first tumbling steps into the world of <a href=\"http:\/\/doena-journal.net\/en\/1465\/wpf-mvvm-viewmodel-model-messageboxes\">WPF &amp; MVVM<\/a>.<\/p>\n<p>Now a friend of mine &#8211; who&#8217;s been working in that very field &#8211; has challlenged me:<\/p>\n<p>a) My GUI should remain responsive while the merge is being executed.<\/p>\n<p>b) I should not re-evaluate the entire GUi after every single user action.<\/p>\n<p><!--more--><\/p>\n<p>These two challenges have very real-world reasons. The user doesn&#8217;t like to have the feeling the program hung up. That&#8217;s why you&#8217;re supposed to write an UI that tells the user: &#8220;No, I did not crash, I&#8217;m just not done yet!&#8221;.<\/p>\n<p>And the second one is something that&#8217;s just not noticable on small demo apps such as this one but becomes a problem with bigger GUI: The evaluation of every control after every action can cost a lot of time and performance.<\/p>\n<p>Both can be summarized under the acronym UX which stands for &#8220;User eXperience&#8221;.<\/p>\n<p>OK, it&#8217;s relatively easy to solve challenge a) with the Task Library that comes with .Net Framework 4.5. And even before that you would have simply used a BackgroundWorker thread.gel\u00f6st.<\/p>\n<p>But once you moved a job into a background thread you suddenly have a problem that wasn&#8217;t there before: The user can suddenly continue to use the program and click the same button twice before the first task is completed.<\/p>\n<p>Now you have to think about what the user is allowed to do while the task is still running. Fortunately, in my case that&#8217;s really simple: Nothing. The UI shall just not appear frozen while the task is working.<\/p>\n<p>To deliberately prolong the running time of the task, I added a sleep of 5 seconds in the model so one can actually see something happening. Otherwise the mode stays the same in comparison to the first implementation.<\/p>\n<p>In the ZIP file linked below you&#8217;ll find the code change to use a Task in MVVM_responsive.<\/p>\n<p>Opposed to many other examples you&#8217;ll find on the internet, you shouldn&#8217;t use Task.Factory.StartNew() unless you have really good reasons for it. Just use Task.Run() instead. <a href=\"<a href=\"http:\/\/blog.stephencleary.com\/2013\/08\/startnew-is-dangerous.html\">&#8220;>Here is explained why.<\/a><\/p>\n<p>You also need to consider the fact that we&#8217;re now working in a multi-threaded environment. So we should carefully encapsulate our access to the UI. Thankfully we already interface&#8217;d all accesses in the previous implementation. Check out the changes in UIServicesWPF.cs.<\/p>\n<p>But now for part b) of the challenge.<\/p>\n<p>If you steer away from the global CommandManager you&#8217;re once again responsible to determine dependencies between controls and their values and states and to update them.<\/p>\n<p>In my case the merge button shall only be active if the text boxes are filled in. I.e the state of Button.Enabled is dependent on the value of TextBox.Text.<\/p>\n<p>In MVVM_NoRequery and MVVM_NoRequery_alternate I&#8217;ve shown two ways to do it.<\/p>\n<p>As always, the complexity depends on how many controls are dependent on each other in how many ways.<\/p>\n<p>The solution for bigger and more complex ViewModels is shown on <a href=\"http:\/\/stackoverflow.com\/questions\/1751966\/commandmanager-invalidaterequerysuggested-isnt-fast-enough-what-can-i-do\">StackOverflow<\/a>.<\/p>\n<p><a href=\"http:\/\/doena-journal.net\/wp-content\/uploads\/2014\/11\/MVVM_responsive.zip\">MVVM_responsive.zip<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Deutsche Version A few days ago I wrote about my first tumbling steps into the world of WPF &amp; MVVM. Now a friend of mine&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6,870],"tags":[1454,1453,1730,1729],"class_list":["post-1468","post","type-post","status-publish","format-standard","hentry","category-misc","category-software","tag-net","tag-c","tag-mvvm","tag-wpf"],"_links":{"self":[{"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/posts\/1468","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/comments?post=1468"}],"version-history":[{"count":8,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/posts\/1468\/revisions"}],"predecessor-version":[{"id":1478,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/posts\/1468\/revisions\/1478"}],"wp:attachment":[{"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/media?parent=1468"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/categories?post=1468"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/doena-journal.net\/en\/wp-json\/wp\/v2\/tags?post=1468"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}