将 DispatcherTimer 与异步调用结合使用

发布于 2024-08-31 22:58:58 字数 4048 浏览 8 评论 0原文

我们在使用 WCF 和实体框架的 Silverlight 应用程序中遇到一个问题,只要用户通过关闭网页或浏览器而不是关闭 silverlight 应用程序来关闭应用程序,我们就需要捕获该事件。这是为了验证是否进行了任何更改,在这种情况下,我们会在离开之前询问用户是否要保存。

我们能够完成捕获网页关闭的部分:我们在应用程序对象中编写了一些代码,让网页调用 silverlight 应用程序对象中的方法。当我们在此方法中对 Web 服务进行异步调用以验证是否发生更改 (IsDirty) 时,问题就开始了。我们使用 DispatcherTimer 来检查异步调用的返回。问题是异步调用永远不会完成(在调试模式下,它永远不会最终进入 _BfrServ_Customer_IsDirtyCompleted 方法),而在我们添加此新功能之前它曾经工作正常。

您将在下面找到我们正在使用的代码。

我是编写定时器与异步调用相结合的新手,所以我可能做错了什么,但我不知道是什么。我也尝试了其他方法,但没有成功..

====================== 代码=============== ===============================

''# Code in the application object 

Public Sub New()

  InitializeComponent()

  RegisterOnBeforeUnload()

  _DispatcherTimer.Interval = New TimeSpan(0, 0, 0, 0, 500)

End Sub


Public Sub RegisterOnBeforeUnload()

  ''# Register Silverlight object for availability in Javascript.

  Const scriptableObjectName As String = "Bridge"

  HtmlPage.RegisterScriptableObject(scriptableObjectName, Me)

  ''# Start listening to Javascript event.

  Dim pluginName As String = HtmlPage.Plugin.Id

  HtmlPage.Window.Eval(String.Format("window.onbeforeunload = function () {{ var slApp = document.getElementById('{0}'); var result = slApp.Content.{1}.OnBeforeUnload(); if(result.length > 0)return result;}}", pluginName, scriptableObjectName))

End Sub


Public Function OnBeforeUnload() As String

  Dim userControls As List(Of UserControl) = New List(Of UserControl)

  Dim test As Boolean = True

  If CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0).GetType().Name = "MainPage" Then

    If Not CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).FindName("Tab") Is Nothing Then

      If CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).FindName("Tab").Items.Count >= 1 Then

        For Each item As TabItem In CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).Tab.Items

          If item.Content.GetType().Name = "CustomerDetailUI"

            _Item = item

            WaitHandle = New AutoResetEvent(False)

            DoAsyncCall()

            Exit

          End If

        Next

      End If

    End If

  End If

  If _IsDirty = True Then

    Return "Do you want to save before leaving."

  Else

    Return String.Empty

  End If

End Function


Private Sub DoAsyncCall()

  _Item.Content.CheckForIsDirty(WaitHandle) ''# This code resides in the CustomerDetailUI UserControl - see below for the code

End Sub


Private Sub _DispatcherTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _DispatcherTimer.Tick

  If Not _Item.Content._IsDirtyCompleted = True Then

    Exit Sub

  End If

  _DispatcherTimerRunning = False

  _DispatcherTimer.Stop()

  ProcessAsyncCallResult()

End Sub


Private Sub ProcessAsyncCallResult()

  _IsDirty = _Item.Content._IsDirty

End Sub




''# CustomerDetailUI code

Public Sub CheckForIsDirty(ByVal myAutoResetEvent As AutoResetEvent)

  _AutoResetEvent = myAutoResetEvent

  _BfrServ.Customer_IsDirtyAsync(_Customer) ''# This method initiates asynchroneous call to the web service - all the details are not shown here 

  _AutoResetEvent.WaitOne()

End Sub


Private Sub _BfrServ_Customer_IsDirtyCompleted(ByVal sender As Object, ByVal e As BFRService.Customer_IsDirtyCompletedEventArgs) Handles _BfrServ.Customer_IsDirtyCompleted

  If _IsDirtyFromRefesh Then

    _IsDirtyFromRefesh = False

    If e.Result = True Then

      Me.Confirm("This customer has been modified. Are you sure you want to refresh your data ? " & vbNewLine & " Your changes will be lost.", "Yes", "No", Message.CheckIsDirtyRefresh)

    End If

    Busy.IsBusy = False

  Else

    If e.Result = True Then

      _IsDirty = True

      Me.Confirm("This customer has been modified. Would you like to save?", "Yes", "No", Message.CheckIsDirty)

    Else

      Me.Tab.Items.Remove(Me.Tab.SelectedItem)

      Busy.IsBusy = False

    End If

  End If

  _IsDirtyCompleted = True

  _AutoResetEvent.Set()

End Sub

We have an issue in our Silverlight application which uses WCF and Entity Framework, where we need to trap the event whenever a user shuts down the application by closing the web page or the browser instead of closing the silverlight application. This is in order to verify if any changes have been made, in which case we would ask the user if he wants to save before leaving.

We were able to accomplish the part which consists in trapping the closing of the web page: we wrote some code in the application object that have the web page call a method in the silverlight application object. The problem starts when in this method, we do an asynchroneous call to the Web Service to verify if changes have occured (IsDirty). We are using a DispatcherTimer to check for the return of the asynchroneous call. The problem is that the asynchroneous call never completes (in debug mode, it never ends up stepping into the _BfrServ_Customer_IsDirtyCompleted method), while it used to work fine before we added this new functionality.

You will find belowthe code we are using.

I am new to writing timers in combination with asynchroneous call so I may be doing something wrong but I cannot figure out what. I tried other things also but we without any success..

====================== CODE ==============================================

''# Code in the application object 

Public Sub New()

  InitializeComponent()

  RegisterOnBeforeUnload()

  _DispatcherTimer.Interval = New TimeSpan(0, 0, 0, 0, 500)

End Sub


Public Sub RegisterOnBeforeUnload()

  ''# Register Silverlight object for availability in Javascript.

  Const scriptableObjectName As String = "Bridge"

  HtmlPage.RegisterScriptableObject(scriptableObjectName, Me)

  ''# Start listening to Javascript event.

  Dim pluginName As String = HtmlPage.Plugin.Id

  HtmlPage.Window.Eval(String.Format("window.onbeforeunload = function () {{ var slApp = document.getElementById('{0}'); var result = slApp.Content.{1}.OnBeforeUnload(); if(result.length > 0)return result;}}", pluginName, scriptableObjectName))

End Sub


Public Function OnBeforeUnload() As String

  Dim userControls As List(Of UserControl) = New List(Of UserControl)

  Dim test As Boolean = True

  If CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0).GetType().Name = "MainPage" Then

    If Not CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).FindName("Tab") Is Nothing Then

      If CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).FindName("Tab").Items.Count >= 1 Then

        For Each item As TabItem In CType(CType(Me.RootVisual, StartPage).LayoutRoot.Children.Item(0), MainPage).Tab.Items

          If item.Content.GetType().Name = "CustomerDetailUI"

            _Item = item

            WaitHandle = New AutoResetEvent(False)

            DoAsyncCall()

            Exit

          End If

        Next

      End If

    End If

  End If

  If _IsDirty = True Then

    Return "Do you want to save before leaving."

  Else

    Return String.Empty

  End If

End Function


Private Sub DoAsyncCall()

  _Item.Content.CheckForIsDirty(WaitHandle) ''# This code resides in the CustomerDetailUI UserControl - see below for the code

End Sub


Private Sub _DispatcherTimer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles _DispatcherTimer.Tick

  If Not _Item.Content._IsDirtyCompleted = True Then

    Exit Sub

  End If

  _DispatcherTimerRunning = False

  _DispatcherTimer.Stop()

  ProcessAsyncCallResult()

End Sub


Private Sub ProcessAsyncCallResult()

  _IsDirty = _Item.Content._IsDirty

End Sub




''# CustomerDetailUI code

Public Sub CheckForIsDirty(ByVal myAutoResetEvent As AutoResetEvent)

  _AutoResetEvent = myAutoResetEvent

  _BfrServ.Customer_IsDirtyAsync(_Customer) ''# This method initiates asynchroneous call to the web service - all the details are not shown here 

  _AutoResetEvent.WaitOne()

End Sub


Private Sub _BfrServ_Customer_IsDirtyCompleted(ByVal sender As Object, ByVal e As BFRService.Customer_IsDirtyCompletedEventArgs) Handles _BfrServ.Customer_IsDirtyCompleted

  If _IsDirtyFromRefesh Then

    _IsDirtyFromRefesh = False

    If e.Result = True Then

      Me.Confirm("This customer has been modified. Are you sure you want to refresh your data ? " & vbNewLine & " Your changes will be lost.", "Yes", "No", Message.CheckIsDirtyRefresh)

    End If

    Busy.IsBusy = False

  Else

    If e.Result = True Then

      _IsDirty = True

      Me.Confirm("This customer has been modified. Would you like to save?", "Yes", "No", Message.CheckIsDirty)

    Else

      Me.Tab.Items.Remove(Me.Tab.SelectedItem)

      Busy.IsBusy = False

    End If

  End If

  _IsDirtyCompleted = True

  _AutoResetEvent.Set()

End Sub

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

眸中客 2024-09-07 22:58:58

您的问题是 DispatchTimer 试图在您用 Wait 阻塞的同一线程上执行代码。因此它无法传递蜱虫。

我不确定我是否清楚为什么你需要计时器。为什么不直接在调用 OnBeforeUnload 中直接阻塞 UI 线程(事实上您已经这样做了)。然后让异步回调函数在分配 _IsDirty 的值后设置等待句柄。

按照消息框的“等待”操作。

Your problem is that the DispatchTimer is trying to execute code on the same thread that you are blocking with the Wait. Hence it can't deliver the tick.

I'm not sure I'm clear why you need the timer at all. Why not simply block the UI thread (as in fact you already doing) directly in the call to OnBeforeUnload. Then have the asynchronous callback function set the wait handle after it has assigned the value of _IsDirty.

Follow the Wait with your message boxes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文