0

我正在使用这里找到的库,它有助于在多个线程上呈现 UI。该示例工作正常并且可以执行应有的操作,但我想对其进行一些修改。

在示例中,要在单独的线程上呈现的视觉元素是在后面的代码中定义/创建的。这是一些示例代码:

XAML:

<Window x:Class="VisualTargetDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Microsoft.DwayneNeed.Controls;assembly=Microsoft.DwayneNeed"
    Title="VisualTargetDemo"
    SizeToContent="WidthAndHeight"
    Loaded="OnLoaded"
    >
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <local:VisualWrapper Grid.Column="0" Width="200" Height="100" x:Name="Player1"/>
    <local:VisualWrapper Grid.Column="1" Width="200" Height="100" x:Name="Player2"/>
    <local:VisualWrapper Grid.Column="2" Width="200" Height="100" x:Name="Player3"/>
  </Grid>
</Window>

代码隐藏:

using System;
using System.Collections.Generic;
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.Shapes;
using System.Threading;
using Microsoft.DwayneNeed.Controls;
using Microsoft.DwayneNeed.Threading;

namespace VisualTargetDemo
{
    public partial class Window1 : System.Windows.Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            Player1.Child = CreateMediaElementOnWorkerThread();
            Player2.Child = CreateMediaElementOnWorkerThread();
            Player3.Child = CreateMediaElementOnWorkerThread();
        }

        private HostVisual CreateMediaElementOnWorkerThread()
        {
            // Create the HostVisual that will "contain" the VisualTarget
            // on the worker thread.
            HostVisual hostVisual = new HostVisual();

            // Spin up a worker thread, and pass it the HostVisual that it
            // should be part of.
            Thread thread = new Thread(new ParameterizedThreadStart(MediaWorkerThread));
            thread.SetApartmentState(ApartmentState.STA);
            thread.IsBackground = true;
            thread.Start(hostVisual);

            // Wait for the worker thread to spin up and create the VisualTarget.
            s_event.WaitOne();

            return hostVisual;
        }

        private FrameworkElement CreateMediaElement()
        {
            // Create a MediaElement, and give it some video content.
            MediaElement mediaElement = new MediaElement();
            mediaElement.BeginInit();
            mediaElement.Source = new Uri("http://download.microsoft.com/download/2/C/4/2C433161-F56C-4BAB-BBC5-B8C6F240AFCC/SL_0410_448x256_300kb_2passCBR.wmv?amp;clcid=0x409");
            mediaElement.Width = 200;
            mediaElement.Height = 100;
            mediaElement.EndInit();

            return mediaElement;
        }

        private void MediaWorkerThread(object arg)
        {
            // Create the VisualTargetPresentationSource and then signal the
            // calling thread, so that it can continue without waiting for us.
            HostVisual hostVisual = (HostVisual)arg;
            VisualTargetPresentationSource visualTargetPS = new VisualTargetPresentationSource(hostVisual);
            s_event.Set();

            // Create a MediaElement and use it as the root visual for the
            // VisualTarget.
            visualTargetPS.RootVisual = CreateMediaElement();

            // Run a dispatcher for this worker thread.  This is the central
            // processing loop for WPF.
            System.Windows.Threading.Dispatcher.Run();
        }

        private static AutoResetEvent s_event = new AutoResetEvent(false);
    }
}



所以 OnLoaded 创建并设置了 VisualWrapper 的 Child 属性。我想做的是允许用户直接在 XAML 中定义要分配给 Child 的控件,例如:

<local:VisualWrapper Grid.Column="0" Width="200" Height="100" x:Name="Player1"/>               
  <local:VisualWrapper.ChildSource>
    <Button>TEST</Button>
  </local:VisualWrapper.ChildSource>
</View:VisualWrapper>



计划是将 ChildSource DependencyProperty 添加到 VisualWrapper 类,并且每当该属性更改时,重新创建它的子内容。问题是当我尝试从 VisualWrapper 中访问 ChildSource 控件时:

public FrameworkElement ChildSource
{
    get { return (FrameworkElement)this.GetValue(ChildSourceProperty); }
    set { this.SetValue(ChildSourceProperty, value); }
}

我收到一个错误:“调用线程无法访问此对象,因为不同的线程拥有它。”。我无法让它工作,我想知道它是否可能。



我尝试在 getter 中使用 Dispatcher:

public FrameworkElement ChildSource
{
    get { return (FrameworkElement)Dispatcher.Invoke((Delegate)GetValue(ChildSourceProperty)); }
    set { this.SetValue(ChildSourceProperty, value); }
}

但这没有用。我的感觉是需要一些 Disaptcher.Invoke 但我不确定在哪里。

我想做的事可以做吗?

4

1 回答 1

1

不。

WPF 创建具有线程关联性的对象,并且这些对象只能从创建它们的线程中使用。这意味着如果您想在线程之间拆分 UI,则必须在将要使用它的线程上创建每个控件。

于 2013-06-21T14:34:46.490 回答