C# 将对象所有权传递给WPF应用程序中的其他线程

C# 将对象所有权传递给WPF应用程序中的其他线程,c#,wpf,multithreading,c#-4.0,C#,Wpf,Multithreading,C# 4.0,编辑1: 显然,我已经开始以错误的方式在WPF环境中实现3D渲染。Ofc下面我的问题有一个解决方案,但我建议阅读Sheridan答案的更新,并使用他的建议来实现这一点。它不仅安全,而且性能更好。虽然理解它有点复杂,但一旦理解它,就可以开始在WPF中渲染多个3D应用程序。 谢里登,谢谢你的帮助 问题, 我是WPF的新手,我想用WPF设计一个连续渲染(就像在游戏应用程序中一样)。我使用多线程来提供更好的UI控件(启动/停止按钮fe)。或者,可以通过使用无限循环渲染3D世界来处理事件 但是,我的问题

编辑1:

显然,我已经开始以错误的方式在WPF环境中实现3D渲染。Ofc下面我的问题有一个解决方案,但我建议阅读Sheridan答案的更新,并使用他的建议来实现这一点。它不仅安全,而且性能更好。虽然理解它有点复杂,但一旦理解它,就可以开始在WPF中渲染多个3D应用程序。 谢里登,谢谢你的帮助


问题,

我是WPF的新手,我想用WPF设计一个连续渲染(就像在游戏应用程序中一样)。我使用多线程来提供更好的UI控件(启动/停止按钮fe)。或者,可以通过使用无限循环渲染3D世界来处理事件

但是,我的问题是,在运行程序时,我得到了一个
无效操作未处理的错误。问题是有一个对象是主线程的属性,因此新线程可能无法访问它

从XAML文件

<Grid>
    <!-- ui controls omitted ... -->
    <Viewport3D Name="myViewport" ClipToBounds="True">
        <!-- all inits, camera, pos, ... -->
    </Viewport3D>
</Grid>
currViewport.Children.Count方法中捕获错误。如前所述,问题在于当前线程没有该对象的所有权。这是我在多线程处理经验中第一次面对这个问题。我四处寻找,但找不到解决办法


有人知道如何传递Viewport3D对象的所有权,或者一个好的解决方法吗?

首先,我想说的是,WPF不是一个开发除最简单的游戏之外的所有游戏的好框架。。。我建议改用微软这样的游戏框架

但是,如果您坚持使用WPF,那么我想提请您注意这个事件。它基本上使用主机的帧速率来渲染图形过程,避免了使用计时器的需要

您还应该查看MSDN上的页面,以获取更多有用的信息和代码示例

另外,请阅读《WPF控制开发释放:构建高级用户体验》一书的摘录:

一些读者可能认识到这种方法和高端图形之间的相似性 像DirectX这样的子系统。不要将CompositionTarget.Rendering误认为是良好的注入 指向创建基于WPF的游戏图形引擎。高端图形和超高分辨率 帧速率不是WPF动画这一特定方面的目标

与Dispatchermer方法类似,基于CompositionTarget.Rendering的动画 也没有时间限制。但是,这些事件将与渲染线程同步 在比分派器更平滑的动画中。此外,也没有必要启动和启动 停止计时器,尽管您可能必须分离并附加事件处理程序以提高性能 表演

更新>>>

在发现这只是一个课程项目之后,我将忽略我之前的评论和到目前为止您的代码示例。当已有渲染系统时,不要尝试创建新的渲染系统。相反,您应该遵循以下方法:

创建实现
INotifyPropertyChanged
接口并具有
X
Y
DirectionVector
(可以是
Size
struct)公共属性的数据对象

添加一个
Move
方法(或
Swim
方法,用于
Fish
类),在该方法中,根据
DirectionVector
属性的值更新数据对象的
X
Y
属性

列表框
控件添加到用户界面

创建集合属性以保存数据对象、添加项并将集合绑定到
列表框.ItemsSource
属性

创建一个
DataTemplate
来定义
Fish
对象的外观。。。您可以使用
Path
类来绘制它们,甚至可以使用
RotateTransform
来旋转它们(可以从
DirectionVector
属性计算角度)。在
DataTemplate
中,可以将
X
Y
属性绑定到“Margin”属性


最后,添加一个无限循环(可能带有一个中断选项),在该循环中,迭代数据对象集合并对每个对象调用
Move()
。这将更新数据对象在
列表框中的位置作为一般规则,在WPF中唯一可以更改线程效忠的对象是那些从
可自由化的
派生的对象。(例如,
Model3D
是可自由化的,因此,
Light
GeometryModel3D
等都是可自由化的)

直接参与可视化树的元素不是从
Freezable
派生的。它们源自视觉(通常,尽管不总是,通过
FrameworkElement
)。因此,视觉元素永远与创建它们的线程相关联。Freezable通常是告诉可视化树元素做什么的描述性项目。例如,画笔(无论是实体、渐变填充、图像画笔还是其他)都是可自由化的,但要使用画笔进行某些操作,您需要将其用作某些视觉元素的属性(即不可自由化的属性),例如,
矩形的
填充

因此
Model3D
属于这一类-它是对3D模型的描述,但实际上不知道如何渲染自己。您可以将此描述提供给某些确实知道如何渲染模型的视觉元素(例如,
Viewport3D

因此,可以在工作线程上构建
Model3D
,然后将其传递给UI线程

但是,只有在通过调用
Freeze/// <summary>this method is done to render the 3D app in other thread.</summary>
private void Runtime(Viewport3D vp) {
    System.Diagnostics.Debug.WriteLine("runtime ");
    Render3D r3d = new Render3D(vp);
    // actual startup
    while (keepRunning) {
        r3d.Init3D();
    }
}

/// <summary>this method toggles the game runtime</summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void StartOrStop(object sender, RoutedEventArgs e) {
    keepRunning = !keepRunning;
    if (keepRunning) {
        buttonStartStop.Content = "Stop";
        // thread
        t1 = new Thread( () => Runtime(myViewport) );
        t1.Start();
    }
    else {
        buttonStartStop.Content = "Start";
        t1.Abort();
    }
}
// constructor
internal Render3D(Viewport3D v) {
    currViewport = v;
}

/// <summary>get called in loops to render gfx</summary>
internal void Init3D() {
    // clear rendered view
    ClearRenderWindow();
    // add landscape
    AddLandScape();
}

/// <summary>clear window to re-render gfx</summary>
private void ClearRenderWindow() {
    ModelVisual3D mv;

    // ***** error got caught here below ******
    for (int i = currViewport.Children.Count - 1; i >= 0; i--) {
        mv = (ModelVisual3D)currViewport.Children[i];
        if (mv.Content is DirectionalLight == false) currViewport.Children.Remove(mv);
    }
}