如何从调度线程访问单独的线程产生WPF UI元素?(How to access separate t

2019-06-26 02:41发布

我需要生成使用WPF像固定文档,FlowDocument的,PageContent,BlockUIContainer以及所有那些UI元素的打印预览(长个)。 为了让我的UI响应我在一个单独的线程类线程执行这一部分(BackgroundWorker的将无法工作,因为我需要一个STA线程)。 一切都高达这点确定。
但是现在显示的打印预览后,我需要打印,并点击生成的预览打印图标抛出了臭名昭著的“因为不同的线程拥有它调用线程不能访问此对象。” 例外。 那么,有没有任何办法解决?

编辑(CODE):

Dispatcher.CurrentDispatcher.Invoke(new Action(() =>  
    {  
        Thread thread = new Thread(() =>  
            {  
                FixedDocument document = renderFlowDocumentTemplate(report);  
                PrintPreview preview = new PrintPreview();  
                preview.WindowState = WindowState.Normal;  
                preview.documentViewer.Document = document;  
                preview.ShowDialog();  
            });  
        thread.SetApartmentState(ApartmentState.STA);  
        thread.Start();  
    }));`

确定这里的RenderFlowDocumentTemplate()生成打印预览(其中包含UI元素),并与报告的数据填充它们。 打印预览是包含实际保存并显示预览,并包含打印图标,在点击我敢supposd得到PrintDialog类窗口的DocumentViewer元素自定义窗口。

编辑(XAML):

<cw:CustomWindow x:Class="MyApp.Reports.PrintPreview"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cw="clr-namespace:MyApp.UI.CustomWindows;assembly=MyApp.UI.CustomWindows">    
    <DocumentViewer Margin="0,30,0,0" Name="documentViewer"></DocumentViewer>
</cw:CustomWindow>`

Answer 1:

最简单的方法是。

Action a = () =>
{
    //Code from another thread.
};
Dispatcher.BeginInvoke(a);


Answer 2:

我前一段时间追平这个 - 我认为发现问题是在打印预览对话框必须在mainthread。



Answer 3:

发现有完全相同的问题另一个男人- 打印的DocumentViewer的在不同的UI线程的内容 。 只是跟着相同的路径。 该代码这里是一个真正的救星。
现在,我并不想从调度线程访问的辅助线程产生的UI元素,而不是现在的印刷过程的其余部分是辅助线程上执行。 没有跨线程UI元素的“VerifyAccess”,和它的工作顺利进行。 :)



Answer 4:

还有另一个“绝招” ...

我经常碰到同样的问题。 例如,我尝试绑定FrameworkElement一个ContentPresenter 。 我对这个解决方案,我用ItemsControl到位的ContentPresenter和我单独绑定FrameworkElement超过一个ObservableCollection<FrameworkElement>只用一个项目。 在此之后,没有问题



Answer 5:

我已经写了这个简单的代码片段,我没有任何经验与它,但我测试了它的一些东西,这似乎是工作的罚款。

/// <summary>
/// Creates UI element on a seperate thread and transfers it to
/// main UI thread. 
/// 
/// Usage; if you have complex UI operation that takes a lot of time, such as XPS object creation.
/// </summary>
/// <param name="constructObject">Function that creates the necessary UIElement - will be executed on new thread</param>
/// <param name="constructionCompleted">Callback to the function that receives the constructed object.</param>
public void CreateElementOnSeperateThread(Func<UIElement> constructObject, Action<UIElement> constructionCompleted)
{
    VerifyAccess();

    // save dispatchers for future usage.
    // we create new element on a seperate STA thread
    // and then basically swap UIELEMENT's Dispatcher.
    Dispatcher threadDispatcher = null;
    var currentDispatcher = Dispatcher.CurrentDispatcher;

    var ev = new AutoResetEvent(false);
    var thread = new Thread(() =>
        {
            threadDispatcher = Dispatcher.CurrentDispatcher;
            ev.Set();

            Dispatcher.Run();
        });

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

    ev.WaitOne();

    threadDispatcher.BeginInvoke(new Action(() =>
        {
            var constructedObject = constructObject();
            currentDispatcher.BeginInvoke(new Action(() =>
                {
                    var fieldinfo = typeof (DispatcherObject).GetField("_dispatcher",
                                                                       BindingFlags.NonPublic |
                                                                       BindingFlags.Instance);
                    if (fieldinfo != null)
                        fieldinfo.SetValue(constructedObject, currentDispatcher);

                    constructionCompleted(constructedObject);
                    threadDispatcher.BeginInvokeShutdown(DispatcherPriority.Normal);
                }), DispatcherPriority.Normal);
        }), DispatcherPriority.Normal);
}

这里是用法:

 CreateElementOnSeperateThread(() =>
        {
            // running on new temp dispatcher.
            var loadsOfItems = new List<int>();
            for(var i = 0; i < 100000; i++)
                loadsOfItems.Add(i+12);


            var dataGrid = new DataGrid {ItemsSource = loadsOfItems, Width = 500, Height = 500};

            dataGrid.Measure(new Size(500, 500));
            dataGrid.Arrange(new Rect(0, 0, 500, 500));

            return dataGrid;
        }, result => SampleGrid.Children.Add(result));


Answer 6:

在这种情况下,使用调度类。 Dispatcher类具有调用和BeginInvoke的方法。 其允许将请求发送到使用dispatchment过程当前线程。 你需要做的是创建一个像下面使用委托调用

  App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
//you code goes here.
 }));

在开始调用呼叫处理您的通话异步。



文章来源: How to access separate thread generated WPF UI elements from the Dispatcher thread?