【问题标题】:Possible to await XamlReader.LoadAsync()?可以等待 XamlReader.LoadAsync() 吗?
【发布时间】:2013-08-07 17:48:13
【问题描述】:

我有一个程序,用户可以在其中查看保存在 XAML 中的各种 3D 模型,然后使用 TrackballDecorator 操作 Viewport3D。这些 XAML 文件中包含的转换是我使用问题WPF 3D - How can I save and load a Camera view?

的答案中的修改应用于 TrackballDecorator

我最近将项目切换到 .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 方法返回一个 object 而不是 Task&lt;object&gt; 我不能使用 await 关键字。有什么方法可以克服或解决这个限制吗?

【问题讨论】:

  • 异步等待只是语法糖。如果您真的想要它,只需将其包裹在 new TaskFactory().StartNew()
  • 我不确定我是否理解。我需要异步执行 XAML 加载,并且需要在该任务完成后实例化 TrackballDecorator,还有其他方法吗? TaskFactory().StartNew(LoadAsync...) 和 TaskFactory().StartNew(LoadAsync...) 似乎不起作用。
  • LoadAsyncawait 使用的异步方法完全不同。阅读 LoadAsync 的 MSDN 文档:它返回一个将异步添加新元素的文档。在这种情况下,这不是您想要的。
  • 您需要使用 XamlReader.LoadCompleted 事件来判断异步操作何时完成。 msdn.microsoft.com/en-us/library/…
  • Matt 做对了 - 我错过了 XamlReader 上的事件处理程序,它解决了我的问题。

标签: c# wpf asynchronous .net-4.5 async-await


【解决方案1】:

这里有两个问题。

一方面,将 EAP 操作转换为任务的“规范”方法是使用 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 只是第二个问题的解决方法。

【讨论】:

    【解决方案2】:

    我最终使用 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();
    }
    

    【讨论】:

    • TaskCompletionSource 将是比SemaphoreSlim 更轻量级的解决方案。
    【解决方案3】:

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

    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) };
    }
    

    【讨论】:

    • 这不是异步操作。 LoadAsync 返回根对象并在另一个线程上更新它。您的解决方案将阻塞,直到整个树被加载。另一方面,OP 的代码没有使用返回的根对象,因此最终结果是等价的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2019-07-26
    • 1970-01-01
    • 2020-06-30
    相关资源
    最近更新 更多