与Silverlight 5 / Async CTP相关
我想创建一个异步函数来启动布局更新,然后等待布局更新完成。类似的东西:
Private Async Function UpdateLayoutRoot() As Task
LayoutRoot.UpdateLayout()
Await LayoutRoot.LayoutUpdated <--- (NOT valid but shows desired outcome)
End Function
如何做到这一点?更一般地说,如何使用Await等待现有事件?
答案 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分配结果(适用于被监视事件的事件参数),然后运行异步方法中的剩余代码。