C#UWP XAML-更新控件“同步”

在这个问题中,我已经包含了一些链接,这些链接表明我已经完成了一些寻找解决方案的工作。

我正在开发一个带触摸屏和GPIO的UWP应用程序。

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

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

如果这些方法中的步骤将同步执行…可能会通过以下代码描述所需的行为:

//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()”完成后立即完成。

从阅读答案到类似问题 ……似乎“Start()”中的UI更新失败,因为我不在UI线程上(“应用程序称为为不同线程编组的接口。”)。 似乎Task Asynchronous Pattern是这些类型问题的常见答案。 但是,我这样做的尝试产生了奇怪的结果……

下面的代码是最接近我想要的结果。 我添加了使用CoreDispatcher处理UI更新的异步任务。

  //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#……以及它看起来过于复杂和令人困惑的事实):

– 在“Start()”中我对任务使用.Wait()(因为它似乎工作,我真的不明白为什么……),并且在btnReset_Click()中它最好等待它们……

-btnReset_Click()不是同步的。 UI更新似乎是“落后一步”……即,在调试模式下,当我跳过“等待UpdateButton(resetButton,false)”时,停止按钮启用,当我跳过“等待UpdateText(”Motor)时复位按钮禁用转动“)”等等。

-Regarding btnReset_Click()… while循环实时持续时间超过1毫秒,但如果我删除所有“await Task.Delay(1)”,则UI更新“落后一步”。 随着“await Task.Delay(1)”的包含,UI更新被“赶上”到应该的位置。 为什么“等待Task.Delay(1)”以这种方式影响UI更新?

如果有任何知识渊博的人愿意解决这个问题的一些/全部,并且可能让我为他们的答案提供详细信息,我将非常感激!

奖金问题….我还在触摸屏上有一个“切换测试模式”按钮,它可以启用一个UI按钮列表并禁用另一个按钮(基于静态bool“testmode”)。 我不需要在这里使用TAP更新UI ,但回想一下,我想同步执行此操作(即使在此示例中看起来毫无意义)。

  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

如上所述,它的行为类似于btnReset_Click()…其中UI更新是“落后一步”。 但是,如果我在事件处理程序中为每个等待添加“.ConfigureAwait(false)”,那么它将变为同步。 我已经对这个主题做了一些阅读,但还没有完全理解它 ,我希望有更好理解的人帮助我理解它与我的项目有关。

你不应该做任何Dispatcher.Run和类似的……

首先停下来思考并了解您的问题是什么以及为什么UI不会更新。

创建一个控制电机的新线程(与UI线程分开)。

在按钮上单击,在电机螺纹上调用方法。

当您需要更新UI的电机线程上有事件时,在UI线程上调用(同步?)方法。

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

  • 永远不要调用.Wait()Result或以其他方式尝试阻止UI调度程序线程上的异步操作
  • 如果您确实想要创建工作线程来执行阻塞操作,请尝试await Task.Run(...)以简化(您可以创建原始线程,但它更有用)。 繁忙的等待也是如此while(notDone) ; // empty while(notDone) ; // empty
  • 从后台线程(例如由Task.Run创建的Task.Run ),如果要更新UI,那么您将使用Dispatcher.RunAsync(...)但仅用于设置UI的属性
  • 要在后台线程工作时禁用UI,请设置IsEnabled=false或添加最顶级的emi-transparent交互屏蔽等。

另外,尝试从简单的东西开始(例如,没有硬件访问;只需使用Task.Delay(...).Wait()来模拟硬件上的阻塞)。 一旦你的UI基本上工作,你可以插入硬件调用。