使用Async CTP等待事件?

时间:2011-07-15 19:32:44

标签: vb.net asynchronous silverlight-5.0

与Silverlight 5 / Async CTP相关

我想创建一个异步函数来启动布局更新,然后等待布局更新完成。类似的东西:

    Private Async Function UpdateLayoutRoot() As Task
       LayoutRoot.UpdateLayout()
       Await LayoutRoot.LayoutUpdated  <--- (NOT valid but shows desired outcome)           
    End Function

如何做到这一点?更一般地说,如何使用Await等待现有事件?

2 个答案:

答案 0 :(得分:3)

实现此目的的一种方法是等待事件中设置的TaskCompletionSource。我不懂VB.NET,希望你能从C#中理解它:

// The type and value returned by the TaskCompletionSource
// doesn't matter, so I just picked int.
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();

// The delegate sets the TaskCompletionSource -- the result value
// doesn't matter, we only care about setting it.  Keep hold of
// the delegate so it can be removed later.
EventHandler d = (o, e) => { tcs.TrySetResult(1); };

LayoutRoot.LayoutUpdate += d;

try
{
    LayoutRoot.UpdateLayout();
    await tcs.Task;
}
finally
{
    // Don't leak the delegate!
    LayoutRoot.LayoutUpdate -= d;
}

答案 1 :(得分:2)

谢谢科里!您使用TaskCompletionSource的建议正是我所需要的。我结合使用TaskCompletionSource和Lucian Wischik's Async CTP specification来开发一对通用的Silverlight 5类,可以用来等待任何CLR或路由事件。只需要Async CTP(AsyncCtpLibrary_Silverlight)(不需要强大的Rx库)。以下是两个类:

Public Class AwaitableEvent(Of TResult)

    Private eta As EventTaskAwaiter(Of TResult) = Nothing

    Sub New(ByVal Sender As Object, ByVal EventName As String)
        eta = New EventTaskAwaiter(Of TResult)
        Dim ei as EventInfo = Sender.GetType.GetEvent(EventName)
        Dim d = [Delegate].CreateDelegate(ei.EventHandlerType, 
            Me, "EventCompletedHandler", True, True)
        ei.AddEventHandler(Sender, d)
    End Sub

    Public Function GetAwaiter() As EventTaskAwaiter(Of TResult)
        Return eta
    End Function

    Private Sub EventCompletedHandler(ByVal sender As Object, ByVal e As TResult)
        eta.tcs.TrySetResult(e)
    End Sub

End Class

Public Class EventTaskAwaiter(Of TResult)

    Friend tcs As New TaskCompletionSource(Of TResult)

    Public ReadOnly Property IsCompleted As Boolean
        Get
            Return tcs.Task.IsCompleted
        End Get
    End Property

    Sub OnCompleted(r As Action)
        Dim sc = SynchronizationContext.Current
        If sc Is Nothing Then
            tcs.Task.ContinueWith(Sub() r())
        Else
            tcs.Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
        End If
    End Sub

    Function GetResult() As TResult
        If tcs.Task.IsCanceled Then Throw New TaskCanceledException(tcs.Task)
        If tcs.Task.IsFaulted Then Throw tcs.Task.Exception.InnerException
        Return tcs.Task.Result
    End Function

End Class

以下是使用AwaitableEvent类来等待鼠标,键盘和计时器事件的示例。

Private Sub AECaller()
    GetMouseButtonAsync()
    MessageBox.Show("After Await mouse button event")
    GetKeyAsync()
    MessageBox.Show("After Await key event")
    GetTimerAsync()
    MessageBox.Show("After Await timer")
End Sub

Private Async Sub GetMouseButtonAsync()
    Dim ae As New AwaitableEvent(Of MouseButtonEventArgs)(LayoutRoot, "MouseLeftButtonDown")
    Dim e = Await ae
    MessageBox.Show(String.Format("Clicked {0} at {1},{2}",
                                  e.OriginalSource.ToString,
                                  e.GetPosition(LayoutRoot).X,
                                  e.GetPosition(LayoutRoot).Y))
End Sub

Private Async Sub GetKeyAsync()
    Dim ae As New AwaitableEvent(Of KeyEventArgs)(LayoutRoot, "KeyDown")
    Dim e = Await ae
    MessageBox.Show(String.Format("Key {0} was pressed", e.Key.ToString))
End Sub

Private Async Sub GetTimerAsync()
    Dim StopWatch As New DispatcherTimer
    StopWatch.Interval = New TimeSpan(TimeSpan.TicksPerSecond * 6)
    Dim ae As New AwaitableEvent(Of EventArgs)(StopWatch, "Tick")
    StopWatch.Start()
    Await ae
    MessageBox.Show(String.Format("It's {0}seconds later!", StopWatch.Interval.TotalSeconds))
    StopWatch.Stop()
End Sub

正如预期的那样,Await语句立即将控制权返回给调用函数。当事件随后完成时,Await分配结果(适用于被监视事件的事件参数),然后运行异步方法中的剩余代码。