星期一, 6月 01, 2020

[C#] 更新主執行緒控件

Thread 執行完後,不可以直接去更改 Main Thread 上的控件,這概念聽過好幾次,筆記可以理解的使用方式,沒有包袱的話,以 async、await 為主,簡單好用

Xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="AsyncAwait.UIControlUpdate">
    <ContentPage.Content>
        <StackLayout>
            <Button x:Name="ThreadExceptionButton" Text="執行 Thread 並拋出 Thread Excption"/>
            <Button x:Name="ThreadActionButton" Text="執行 Thread 並透過 Action 來更新控件"/>
            <Button x:Name="AsyncAwaitButton" Text="Async、Await 來更新控件"/>
            <Button x:Name="EssentialsButton" Text="Essentials 來更新控件"/>
            <Label x:Name="lblResult" Text="顯示結果" FontSize="Large" HorizontalOptions="Center" />
        </StackLayout>
    </ContentPage.Content>
</ContentPage>
C# Code
using System;
using System.Threading;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using Xamarin.Essentials;

namespace AsyncAwait
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class UIControlUpdate : ContentPage
    {
        public UIControlUpdate()
        {
            InitializeComponent();
            ThreadExceptionButton.Clicked += (sender, e) => ThreadException();
            ThreadActionButton.Clicked += (sender, e) => ThreadAction();
            AsyncAwaitButton.Clicked += (sender, e) => AsyncAwait();
            EssentialsButton.Clicked += (sender, e) => Essentials();
        }

        private void ThreadException()
        {
            int waiting = 0;
            Thread t1 = new Thread(() =>
            {
                waiting = Waiting();

                // Xamarin 上的 Exception 錯誤訊息
                // Android.Util.AndroidRuntimeException
                // Message = Only the original thread that created a view hierarchy can touch its views.
                lblResult.Text = $"等待時間為 {waiting} ";
            });
            t1.Start();

            lblResult.Text = "等待中......";
        }

        private void ThreadAction()
        {
            int waiting = 0;
            Thread t1 = new Thread(() =>
            {
                waiting = Waiting();

                Action action = () => lblResult.Text = $"等待時間為 {waiting}";

                // Xamarin 寫法
                Device.BeginInvokeOnMainThread(action);
            });
            t1.Start();

            lblResult.Text = "等待中......";
        }

        private async void AsyncAwait()
        {
            Task<int> task = Task.Run(() => Waiting());
            lblResult.Text = "等待中......";
            int waiting = await task;
            lblResult.Text = $" 等待時間為 {waiting}";
        }

        private async void Essentials()
        {
            lblResult.Text = "等待中......";

            await Task.Run(() => 
            {
                int waiting = Waiting();

                // Xamarin.Essentials MainThread 寫法
                MainThread.BeginInvokeOnMainThread(() => 
                { 
                    lblResult.Text = $" 等待時間為 {waiting}";
                });
            });
        }

        /// <summary>
        /// 等待 5 秒
        /// </summary>
        /// <returns>5 秒</returns>
        private int Waiting()
        {
            int waitSecond = 5000;
            Thread.Sleep(waitSecond);
            return waitSecond;
        }
    }
}
UI 畫面:四個按鈕和一個標籤

[C#] 更新主執行緒控件-1 

操作 ThreadException() 會拋出 Exception 

[C#] 執行緒更新控件-2

操作 ThreadAction()、AsyncAwait() 和 Essentials() 畫面如下

[C#] 更新主執行緒控件-3

[C#] 更新主執行緒控件-4

BeginInvokeOnMainThread 

在 Xamarin 內有兩種方式可以呼叫 Main Thread 分別為 
  • Device.BeginInvokeOnMainThread(Action) 
  • MainThread.BeginInvokeOnMainThread(Action) 
 下圖為官方文章,說明兩者使用時機

沒有留言:

張貼留言