是否可以在不同的线程中初始化WPF UserControls? [英] Is it possible to initialize WPF UserControls in different threads?

查看:116
本文介绍了是否可以在不同的线程中初始化WPF UserControls?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在开发一个WPF应用程序,它将同时打开许多报告(就像典型的MDI应用程序,例如Excel或Visual Studio).尽管可以在多个工作线程中运行这些报表的数据上下文,但我们仍然发现,如果打开的报表的数量确实很大,那么即使是这些报表的呈现(基本上是在MDI环境中或仅在MDI环境中托管的UserControl)主视图中的网格区域)仍会使应用程序的响应速度降低.

We are developing a WPF application which will open a number of reports at the same time (just like a typical MDI application such as Excel or Visual Studio). Although it is possible to have the data context for those reports run in multiple worker threads, we still find that if the number of opened reports is really big, even the rendering of those reports (basically UserControl hosted either in a MDI environment or just in a grid area in the main view) will still make the application less responsive.

因此,我的想法是在主UI中至少包含几个区域,每个区域中的用户控件都将在不同的UI线程中运行.再次,想象一下Visual Studio中的典型视图,除了菜单外,它具有文本编辑器的主要区域,一个侧边区域(例如,托管解决方案资源管理器)和一个底部区域(例如,托管错误列表和输出).因此,我希望这三个区域可以在三个UI线程中运行(但是自然地,它们托管在一个MainView中,这是我不确定的部分).

So, my idea is to at least have several areas in the main UI, each of whom will have its user control running in different UI threads. Again, imagine a typical view in visual studio, except for the menus, it has the main area of text editor, a side area which hosts for example solution explorer, and a bottom area which hosts for example error list and output. So I want these three areas to be running in three UI threads (but naturally they are hosted in one MainView, that's the part I am not sure about).

我问,因为我知道有可能在不同的UI线程中运行多个(顶层)窗口.但是有人说这不适用于用户控件.是真的吗如果是这样,我的方案的典型解决方案是什么,即打开的UserControl的数量确实很大,并且其中许多UserControl是实时的,因此渲染它们需要占用大量资源?谢谢!

I am asking because I know it is possible to have several (top-level) windows running in different UI threads. But somebody said it doesn't apply to the user controls. Is it true? If so, what is the typical solution to my scenario, i.e., the number of opened UserControl is really big, and many of these UserControl are real-time so rendering of them takes huge amount of resources? Thanks!

推荐答案

有关UI线程模型的背景信息

通常,应用程序具有一个主" UI线程...并且它可能具有0个或多个后台/工作线程/非UI线程,您(或.NET运行时/框架)在其中进行后台工作.

Background Information on UI Threading Models

Normally an application has one "main" UI thread...and it may have 0 or more background/worker/non-UI threads where you (or the .NET runtime/framework) does background work.

(...... WPF中还有一个特殊的线程,称为渲染线程,但我暂时将其跳过...)

(...there's a another special thread in WPF called the rendering thread but I will skip that for now...)

例如,一个简单的WPF应用程序可能具有以下线程列表:

For example, a simple WPF Application might have this list of threads:

一个简单的WinForms应用程序可能具有以下线程列表:

And a simple WinForms Application might have this list of threads:

在创建元素时,该元素将绑定(具有亲和力)到特定的Dispatcher&线程,并且只能从与Dispatcher关联的线程安全地访问.

When you create an element it is tied (has affinity) to a particular Dispatcher & thread and can only be accessed safely from the thread associated with the Dispatcher.

如果您尝试从其他线程访问对象的属性或方法,通常会遇到异常,例如在WPF中:

If you try and access properties or methods of an object from a different thread, you will usually get an exception e.g. in WPF:

在WindowsForms中:

In WindowsForms:

http://www.perceler.com/articles1.php? art = crossthreads1

对UI的任何修改都需要在创建UI元素的同一线程上进行...因此,后台线程使用Invoke/BeginInvoke来使工作在UI线程上运行.

Any modifications to the UI need to be performed on the same thread on which a UI element was created...so background threads use Invoke/BeginInvoke to get that work run on the UI thread.

<Window x:Class="WpfApplication9.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
    <StackPanel x:Name="mystackpanel">

    </StackPanel>
</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;

namespace WpfApplication9
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        Thread m_thread1;
        Thread m_thread2;
        Thread m_thread3;
        Thread m_thread4;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            CreateAndAddElementInDifferentWays();
        }

        void CreateAndAddElementInDifferentWays()
        {
            string text = "created in ui thread, added in ui thread [Main STA]";
            System.Diagnostics.Debug.WriteLine(text);

            CreateAndAddTextChild(text);

            // Do NOT use any Joins with any of these threads, otherwise you will get a
            // deadlock on any "Invoke" call you do.

            // To better observe and focus on the behaviour when creating and
            // adding an element from differently configured threads, I suggest
            // you pick "one" of these and do a recompile/run.

            ParameterizedThreadStart paramthreadstart1 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
            m_thread1 = new Thread(paramthreadstart1);
            m_thread1.SetApartmentState(ApartmentState.STA);
            m_thread1.Start("[STA]");

            //ParameterizedThreadStart paramthreadstart2 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
            //m_thread2 = new Thread(paramthreadstart2);
            //m_thread2.SetApartmentState(ApartmentState.STA);
            //m_thread2.Start("[STA]");

            //ParameterizedThreadStart paramthreadstart3 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnThread);
            //m_thread3 = new Thread(paramthreadstart3);
            //m_thread3.SetApartmentState(ApartmentState.MTA);
            //m_thread3.Start("[MTA]");

            //ParameterizedThreadStart paramthreadstart4 = new ParameterizedThreadStart(this.WorkCreatedOnThreadAddedOnUIThread);
            //m_thread4 = new Thread(paramthreadstart4);
            //m_thread4.SetApartmentState(ApartmentState.MTA);
            //m_thread4.Start("[MTA]");
        }

        //----------------------------------------------------------------------

        void WorkCreatedOnThreadAddedOnThread(object parameter)
        {
            string threadingmodel = parameter as string;

            string text = "created in worker thread, added in background thread, " + threadingmodel;
            System.Diagnostics.Debug.WriteLine(text);

            CreateAndAddTextChild(text);
        }

        void WorkCreatedOnThreadAddedOnUIThread(object parameter)
        {
            string threadingmodel = parameter as string;

            string text = "created in worker thread, added in ui thread via invoke" + threadingmodel;
            System.Diagnostics.Debug.WriteLine(text);

            TextBlock tb = CreateTextBlock(text);
            if (tb != null)
            {
                // You can alternatively use .Invoke if you like!

                DispatcherOperation dispop = Dispatcher.BeginInvoke(new Action(() =>
                {
                    // Get this work done on the main UI thread.

                    AddTextBlock(tb);
                }));

                if (dispop.Status != DispatcherOperationStatus.Completed)
                {
                    dispop.Wait();
                }
            }
        }

        //----------------------------------------------------------------------

        public TextBlock CreateTextBlock(string text)
        {
            System.Diagnostics.Debug.WriteLine("[CreateTextBlock]");

            try
            {
                TextBlock tb = new TextBlock();
                tb.Text = text;
                return tb;
            }
            catch (InvalidOperationException ex)
            {
                // will always exception, using this to highlight issue.
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }

            return null;
        }

        public void AddTextBlock(TextBlock tb)
        {
            System.Diagnostics.Debug.WriteLine("[AddTextBlock]");

            try
            {
                mystackpanel.Children.Add(tb);
            }
            catch (InvalidOperationException ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message);
            }
        }

        public void CreateAndAddTextChild(string text)
        {
            TextBlock tb = CreateTextBlock(text);
            if (tb != null)
                AddTextBlock(tb);
        }
    }
}

辅助UI线程,也称为在另一个线程上创建顶级窗口"

创建辅助UI线程是可能的,只要您将该线程标记为使用STA公寓模型,然后创建Dispatcher(例如,使用Dispatcher.Current)并启动运行"循环(Dispatcher.Run() ),因此Dispatcher可以为在该线程上创建的UI元素提供消息.

Secondary UI thread aka "Creating a top-level Window on another thread"

It's possible to create secondary UI-threads, so long as you mark the thread as using the STA apartment model, and create a Dispatcher (e.g. use Dispatcher.Current) and start a "run" loop (Dispatcher.Run()) so the Dispatcher can service messages for the UI elements created on that thread.

http ://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/

http://www.diranieh.com/NET_WPF/Threading.htm

但是不能在一个UI线程中创建一个元素 在不同的元素上创建的另一个元素的逻辑/可视树 用户界面线程.

BUT an element created in one UI thread can't be put into the logical/visual tree of another element which is created on a different UI thread.

解决方法,用于混合在不同的UI线程上创建的元素

有一种有限的解决方法,它可以为您提供一些功能,使您可以使用HostVisual将在一个UI线程中创建的元素与在另一个线程中创建的可视树进行组合.参见以下示例:

Workaround Technique for mixing elements created on different UI threads

There is a limited workaround technique, which may provide you with some ability to compose the rendering of an element created in one UI thread with the visual tree created in a different thread...by using HostVisual. See this example:

这篇关于是否可以在不同的线程中初始化WPF UserControls?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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