C#UWP XAML-“同步"更新控件 [英] C# UWP XAML- Updating controls "synchronously"

查看:99
本文介绍了C#UWP XAML-“同步"更新控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这个问题中,我包括了一些链接,这些链接表明我已经做了一些工作来寻找解决方案.

Throughout this question, I've included some links which show that I've done some work searching for a solution.

我正在开发具有触摸屏和GPIO的UWP应用.

I'm developing a UWP app with touchscreen and GPIO.

UI具有一个停止按钮,一个重置按钮和一个文本块. GPIO用于物理启动按钮,电机和2个限位开关.电动机可以旋转,直到碰到限位开关为止.

UI has a stop button, a reset button, and a textblock. GPIO is used for a physical start button, a motor, and 2 limit switches. Motor can rotate until it runs into a limit switch.

用于控制硬件的代码(例如Motor.Forward())已经过编写和测试,但为简洁起见,此问题已将其排除在外.出于相同的原因,排除了停止按钮的代码.

Code to control the hardware (e.g., Motor.Forward()) has been written and tested, but is excluded from this question for brevity. Code for the stop button is excluded for the same reason.

如果这些方法中的步骤可以同步执行...所需的行为可能由以下代码描述:

If the steps in these methods would perform synchronously... desired behavior might be described by the following code:

 //Event handler for when physical start button is pushed
 private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
 {
     Start();
 }

 private void Start()
 {
      //Update UI
      stopButton.IsEnabled = true;
      resetButton.IsEnabled = false;
      textBlock.Text = "Motor turning";

      Motor.Forward();
      while(Motor.HasNotHitEndLimit())
      Motor.Off();

      //Update UI
      stopButton.IsEnabled = false;
      resetButton.IsEnabled = true;
      textBlock.Text = "Task complete";
 }

 //Event handler for reset button
 private void btnReset_Click()
 {
      //Update UI
      stopButton.IsEnabled = true;
      resetButton.IsEnabled = false;
      textBlock.Text = "Motor turning";

      Motor.Back();
  while(Motor.HasNotHitStartLimit())
      Motor.Off();

      //Update UI
      stopButton.IsEnabled = false;
      resetButton.IsEnabled = true;
      textBlock.Text = "Reset complete";
 }

如果我没记错的话,"private void btnReset_Click()"中的UI更新可以正常工作,但是它们不是同步的……也就是说,所有的UI更新都在"btnReset_Click()"完成后立即完成.

If I recall correctly, UI updates within "private void btnReset_Click()" work, but they are not synchronous... I.e., all of the UI updates were completing right after "btnReset_Click()" finished.

从阅读类似问题的答案中 ...似乎UI在内部更新"Start()"失败,因为我不在UI线程上(该应用程序调用了已编组为另一个线程的接口."). 任务异步模式似乎是这些类型问题的常见答案.但是,我的尝试却产生了奇怪的结果……

From reading answers to similar questions... it seems that UI updates within "Start()" fail because I'm not on the UI thread ("The application called an interface that was marshalled for a different thread."). It seems that Task Asynchronous Pattern is a common answer to these types of questions. However, my attempts to do this have yielded strange results...

下面的代码是我最接近期望结果的代码.我添加了使用CoreDispatcher来处理UI更新的异步任务.

The code below is the closest I've come to the desired result. I added async tasks that use CoreDispatcher to handle UI updates.

 //Task for updating the textblock in the UI
 private async Task UpdateText(string updateText)
 {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
          new DispatchedHandler(() => { textBlock.Text = updateText; }));
 }

 //Task for enable/disable a given button
 private async Task UpdateButton(Button btn, bool shouldThisBeEnabled)
 {
      await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
          new DispatchedHandler(() => { btn.IsEnabled = shouldThisBeEnabled; }));
 }

 //Event handler for when physical start button is pushed
 private async void StartButtonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
 {
     Start();
 }

 private void Start()
 {
      //Update UI
      UpdateButton(stopButton,true).Wait();
      UpdateButton(resetButton,false).Wait();
      UpdateText("Motor turning").Wait();

      Motor.Forward();
  while(Motor.HasNotHitEndLimit())
          Task.Delay(1).Wait();
      Motor.Off();

      //Update UI
      UpdateButton(stopButton,false).Wait();
      UpdateButton(resetButton,true).Wait();
      UpdateText("Task complete").Wait();
 }

 //Event handler for reset button
 private async void btnReset_Click()
 {
      //Update UI
      await UpdateButton(stopButton,true);
      await UpdateButton(resetButton,false);
      await UpdateText("Motor turning");
      await Task.Delay(1);

      Motor.Back();
  while(Motor.HasNotHitStartLimit())
          await Task.Delay(1);
      Motor.Off();

      //Update UI
      await UpdateButton(stopButton,false);
      await UpdateButton(resetButton,true);
      await UpdateText("Reset complete");
 }

上面的代码存在问题/特质(除了刚开始使用C#可能引起的任何初学者错误,以及它似乎过于复杂和令人困惑的事实)

Problems/idiosyncrasies with the code above (besides any beginner mistakes I might be making due to just starting out with C#... and the fact that it seems overly complicated and confusing):

-在"Start()"中,我在任务上使用.Wait()(因为它似乎有效,所以我不太明白为什么...),而在btnReset_Click()中,等待它们的效果最好. ..

-In "Start()" I use .Wait() on the tasks (because it seems to work, I don't really understand why...), and in btnReset_Click() it worked best to await them...

-btnReset_Click()不同步. UI更新似乎落后一步" ...即,在调试模式下,当我越过"await UpdateButton(resetButton,false)"时,停止按钮启用,当我越过"await UpdateText("Motor"时,重置按钮禁用转向)",依此类推.

-btnReset_Click() is not synchronous. UI updates appear to be "one step behind"... I.e., in debug mode, the stop button enables when I step over "await UpdateButton(resetButton,false)", reset button disables when I step over "await UpdateText("Motor turning")", and so on.

-关于btnReset_Click()... while循环实时持续超过1毫秒,但是如果我删除所有"await Task.Delay(1)",则UI更新将落后一步".使用"await Task.Delay(1)",UI更新将赶上"它们应在的位置.为什么等待Task.Delay(1)"会以这种方式影响UI更新?

-Regarding btnReset_Click()... The while loop lasts MUCH longer than 1 millisecond in real time, yet if I remove all "await Task.Delay(1)" then the UI updates are "one step behind". With "await Task.Delay(1)" included, the UI updates get "caught up" to where they should be. Why does "await Task.Delay(1)" affect UI updates this way?

如果有知识渊博的人愿意解决部分/全部问题,也许让我向他们推荐有关他们答案的详细信息,我将非常感激!

If any knowledgeable folks are willing to address some/all of this question and maybe let me prod them for details about their answer(s), I'd be very grateful!

奖金问题....我在触摸屏上还有一个切换测试模式"按钮,它启用一个UI按钮列表,并禁用另一个UI按钮列表(基于静态布尔"testmode"). 我不需要在这里使用TAP来更新UI ,但请记住,我想同步执行此操作(即使在此示例中似乎毫无意义).

Bonus question.... I also have a "Toggle Test Mode" button on the touchscreen which enables one list of UI buttons and disables another (based on a static bool "testmode"). I don't need to use TAP to update the UI here, but recall that I want to do this synchronously (even though it seems pointless in this example).

 private async void btnTestMode_Click(object sender, RoutedEventArgs e)
 {
     testMode = !testMode;
     if (testMode == true)
     {
         await UpdateButtons(TestModeButtons,true);
         await UpdateButtons(NormalModeButtons,false);
         return;
     }
     await UpdateButtons(TestModeButtons,true);
     await UpdateButtons(NormalModeButtons,false);
 }

 private async Task UpdateButtons(List<Button> btns, enable)
 {
     foreach (var btn in btns)
     {
         await Dispatcher.RunAsync(CoreDispatcherPriority.Normal,
             new DispatchedHandler(() => { btn.IsEnabled = enable; }));
     }
 }

正如上面所写,它的行为类似于btnReset_Click()...,其中UI更新落后一步".但是,如果我将".ConfigureAwait(false)"添加到事件处理程序中的每个等待中,则它将变为同步. 我已经读了一些这个主题,但还没有完全理解,我希望有一个更好的了解的人来帮助我理解它与我的项目有关.

As it's written above, this behaves like btnReset_Click()... where the UI updates are "one step behind". However, if I add ".ConfigureAwait(false)" to each await in the event handler, then it becomes synchronous. I've done some reading on this topic, but don't fully understand it yet, and I would love for someone with a better understanding to help me understand it as it relates to my project.

推荐答案

简而言之,在构建应用程序时请考虑以下提示:

In a nutshell, consider these tips as you build your app:

  • 从不调用.Wait()Result,否则尝试阻止UI调度程序线程上的异步操作
  • 如果您确实想创建辅助线程来执行阻塞操作,请尝试使用await Task.Run(...)来简化操作(您可以创建原始线程,但这会增加工作量).对于像while(notDone) ; // empty
  • 这样的繁忙等待也是如此
  • 从后台线程(例如由Task.Run创建的线程),如果要更新UI,则可以使用Dispatcher.RunAsync(...),但仅用于设置UI的属性
  • 要在后台线程运行时禁用UI,请设置IsEnabled=false或添加最顶部的半透明交互屏蔽等.
  • Never call .Wait() or Result or otherwise try to block on an asynchronous operation on a UI dispatcher thread
  • If you do want to create worker threads to do blocking operations, try await Task.Run(...) for its simplicity (you can create raw threads but it's more work). Same for busy-waits like while(notDone) ; // empty
  • From a background thread (such as one created by Task.Run), if you want to update the UI then you would use Dispatcher.RunAsync(...) but only to set the properties of your UI
  • To disable your UI while a background thread does work, set IsEnabled=false or add a top-most emi-transparent interaction shield etc.

此外,尝试从简单的内容开始(例如,没有硬件访问权限;只需使用Task.Delay(...).Wait()模拟对硬件的阻止).用户界面基本正常工作后,您可以插入硬件调用.

Also, try starting with something simple (eg, no hardware access; just use Task.Delay(...).Wait() to simulate blocking on the hardware). Once you have the UI basically working you can plug in the hardware calls.

这篇关于C#UWP XAML-“同步"更新控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆