溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

在WPF中怎么使用多線程更新UI

發(fā)布時(shí)間:2022-06-23 09:43:45 來(lái)源:億速云 閱讀:206 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“在WPF中怎么使用多線程更新UI”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“在WPF中怎么使用多線程更新UI”吧!

有經(jīng)驗(yàn)的程序員們都知道:不能在UI線程上進(jìn)行耗時(shí)操作,那樣會(huì)造成界面卡頓,如下就是一個(gè)簡(jiǎn)單的示例:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            this.Dispatcher.Invoke(new Action(()=> { }));
            this.Loaded += MainWindow_Loaded;
        }

        private void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            this.Content = new UserControl1();
        }
    }

    class UserControl1 : UserControl
    {
        TextBlock textBlock;

        public UserControl1()
        {
            textBlock = new TextBlock();
            this.Content = textBlock;

            this.Dispatcher.BeginInvoke(new Action(updateTime), null);
        }

        private async void updateTime()
        {
            while (true)
            {
                Thread.Sleep(900);            //模擬耗時(shí)操作

                textBlock.Text = DateTime.Now.ToString();
                await Task.Delay(100);
            }
        }
    }

當(dāng)我們運(yùn)行這個(gè)程序的時(shí)候,就會(huì)發(fā)現(xiàn):由于主線程大部分的時(shí)間片被占用,無(wú)法及時(shí)處理系統(tǒng)事件(如鼠標(biāo),鍵盤等輸入),導(dǎo)致程序變得非常卡頓,連拖動(dòng)窗口都變得不流暢;

如何解決這個(gè)問題呢,初學(xué)者可能想到的第一個(gè)方法就是新啟一個(gè)線程,在線程中執(zhí)行更新:

    public UserControl1()
    {
        textBlock = new TextBlock();
        this.Content = textBlock;

        ThreadPool.QueueUserWorkItem(_ => updateTime());
    }

但很快就會(huì)發(fā)現(xiàn)此路不通,因?yàn)閃PF不允許跨線程訪問程序,此時(shí)我們會(huì)得到一個(gè):"The calling thread cannot access this object because a different thread owns it."的InvalidOperationException異常

在WPF中怎么使用多線程更新UI

那么該如何解決這一問題呢?通常的做法是把耗時(shí)的函數(shù)放在線程池執(zhí)行,然后切回主線程更新UI顯示。前面的updateTime函數(shù)改寫如下:

    private async void updateTime()
    {
        while (true)
        {
            await Task.Run(() => Thread.Sleep(900));
            textBlock.Text = DateTime.Now.ToString();
            await Task.Delay(100);
        }
    }

這種方式能滿足我們的大部分需求。但是,有的操作是比較耗時(shí)間的。例如,在多窗口實(shí)時(shí)監(jiān)控的時(shí)候,我們就需要同時(shí)多十來(lái)個(gè)屏幕每秒鐘各進(jìn)行幾十次的刷新,更新圖像這個(gè)操作必須在UI線程上進(jìn)行,并且它有非常耗時(shí)間,此時(shí)又會(huì)回到最開始的卡頓的情況。

看起來(lái)這個(gè)問題無(wú)法解決,實(shí)際上,WPF只是不允許跨線程訪問程序,并非不允許多線程更新界面。我們大可以對(duì)每個(gè)視頻監(jiān)控窗口單獨(dú)其一個(gè)獨(dú)立的線程,在那個(gè)線程中進(jìn)行更新操作,此時(shí)就不會(huì)影響到主線程。MSDN上有篇文章介紹了詳細(xì)的操作:Multithreaded UI: HostVisual。用這種方式將原來(lái)的程序改寫如下:

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        HostVisual hostVisual = new HostVisual();

        UIElement content = new VisualHost(hostVisual);
        this.Content = content;

        Thread thread = new Thread(new ThreadStart(() =>
        {
            VisualTarget visualTarget = new VisualTarget(hostVisual);
            var control = new UserControl1();
            control.Arrange(new Rect(new Point(), content.RenderSize));
            visualTarget.RootVisual = control;

            System.Windows.Threading.Dispatcher.Run();

        }));

        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();
    }

    public class VisualHost : FrameworkElement
    {
        Visual child;

        public VisualHost(Visual child)
        {
            if (child == null)
                throw new ArgumentException("child");

            this.child = child;
            AddVisualChild(child);
        }

        protected override Visual GetVisualChild(int index)
        {
            return (index == 0) ? child : null;
        }

        protected override int VisualChildrenCount
        {
            get { return 1; }
        }
    }

這個(gè)里面用來(lái)了兩個(gè)新的類:HostVisual、VisualTarget。以及自己寫的一個(gè)VisualHost。MSDN上相關(guān)的解釋,也不算難理解,這里就不多介紹了。最后,再來(lái)重構(gòu)一下代碼,把在新線程中創(chuàng)建控件的方式改寫如下:

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        createChildInNewThread<UserControl1>(this);
    }

    void createChildInNewThread<T>(ContentControl container)
        where T : UIElement , new()
    {
        HostVisual hostVisual = new HostVisual();

        UIElement content = new VisualHost(hostVisual);
        container.Content = content;

        Thread thread = new Thread(new ThreadStart(() =>
        {
            VisualTarget visualTarget = new VisualTarget(hostVisual);

            var control = new T();
            control.Arrange(new Rect(new Point(), content.RenderSize));

            visualTarget.RootVisual = control;
            System.Windows.Threading.Dispatcher.Run();

        }));

        thread.SetApartmentState(ApartmentState.STA);
        thread.IsBackground = true;
        thread.Start();
    }

到此,相信大家對(duì)“在WPF中怎么使用多線程更新UI”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI