2

我有一个程序,用户可以在其中查看保存在 XAML 中的各种 3D 模型,然后使用 TrackballDecorator 操作 Viewport3D。这些 XAML 文件中包含的转换是我使用问题WPF 3D - 如何保存和加载相机视图?

我最近将项目切换到 .net 4.5 框架以利用 XamlReader 类的新异步功能,这与我访问模型保存的转换的方式产生了冲突。

我目前正在做的是:

private Task<TrackballDecorator> loadModel(string path)
{
    var tempVP = new XamlReader().LoadAsync(XmlReader.Create(path)) as Viewport3D;
    return new TrackballDecorator() { Content = tempVP, Transform = (tempVP.Camera.Transform as Transform3DGroup) };
}

这不起作用,因为 XamlReader 尚未完成加载,因此返回了一个空 Transform。

我想做的是:

private async Task<TrackballDecorator> loadModelAsync(string path)
{
    var tempVP = await new XamlReader().LoadAsync(XmlReader.Create(path)) as Viewport3D;
    return new TrackballDecorator() { Content = tempVP, Transform = (tempVP.Camera.Transform as Transform3DGroup) };
}

但是因为 LoadAsync 方法返回的是 anobject而不是 aTask<object>我不能使用 await 关键字。有什么方法可以克服或解决这个限制吗?

4

3 回答 3

2

这里有两个问题。

一方面,将 EAP 操作转换为 Task 的“规范”方式是使用TaskCompletionSource并返回源的Task属性。事件处理程序调用SetResult来表示任务已完成并设置其结果。

你可以这样写:

public Task<ViewPort3D> LoadViewPortAsync(string path)
{
     var tcs=new TaskCompletionSource();
     var reader=XmlReader.Create(path);
     ViewPort3D port=null;
     var  xr = new XamlReader();
     xr.LoadCompleted += (o,e)=>{
         tcs.SetResult(port);
         reader.Dispose();
     };
     port=xr.LoadAsync(reader);

     return tcs.Task;
}

获得 ViewPort3D 对象后,您可以创建转换

另一方面,“规范”的 WPF 方式是观察对象的变化并对它们的变化做出反应。LoadAsync 返回根 XAML 对象并向其异步添加节点。由 ViewPort3D 酌情提高 INotifyPropertyChanged 并由 TrackballDecorator 和 Transforms 来对这些更改做出反应。

使用 TaskCompletionSource 只是第二个问题的解决方法。

于 2013-08-07T09:48:13.660 回答
1

我最终使用 XamlReader LoadCompleted 事件处理程序和 SlimSemaphore 解决了这个问题,如下所示:Is it possible to await an event instead of another async method?

private SemaphoreSlim signal = new SemaphoreSlim(0, 1);

private async Task<TrackballDecorator> loadModel(string path) {
    XamlReader xr = new XamlReader();
    xr.LoadCompleted += new AsyncCompletedEventHandler(AsyncXamlComplete);
    var tempVP = xr.LoadAsync(XmlReader.Create(path)) as Viewport3D;

    await signal.WaitAsync();

    return new TrackballDecorator() { Content = tempVP, Transform = tempVP.Camera.Transform as Transform3DGroup) };
}

private void AsyncXamlComplete(Object sender, AsyncCompletedEventArgs e) {
    signal.Release();
}
于 2013-08-06T21:10:45.373 回答
1

您应该能够创建一个任务并等待它

private async Task<TrackballDecorator> loadModelAsync(string path)
{
    var tempVP = await Task.Factory.StartNew<Viewport3D>(() => (Viewport3D)XamlReader.Load(XmlReader.Create(path)));
    return new TrackballDecorator() { Content = tempVP, Transform = (tempVP.Camera.Transform as Transform3DGroup) };
}
于 2013-08-06T21:15:54.397 回答