2011-12-14 5 views
1

У нас есть проблема с приложением, в котором мы вызываем асинхронный процесс на LostFocus в TextBox, асинхронный процесс должен иметь возможность обновлять интерфейс основной формы (или отображать диалог из основного интерфейса UI) во время работы асинхронно.Обновление пользовательского интерфейса из другого потока

Мы думали о обратных вызовах и имели это по-настоящему асинхронное, но нам нужно все, чтобы выполнить в правильном порядке, и это для системы ввода данных, где важна скорость и точность ввода данных.

Пример кода показывает, что мы пытаемся сделать, и если вы переключитесь на BeginInvoke, порядок обработки неверен.

Public Class Form1

Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.LostFocus 

    ' execute the server subroutine 
    Dim dlgt As New MethodInvoker(AddressOf Me.AsyncProcess) 

    TextBox1.Text = "1" 
    ' textbox should say 1 

    ' call the server subroutine asynchronously, so the main thread is free 
    Dim ar As IAsyncResult = dlgt.BeginInvoke(Nothing, Nothing) 

    While ar.IsCompleted = False 
     ' Application.DoEvents() 
    End While 
    ' textbox should now say 2 

    TextBox1.Text = "3" 
    ' textbox should now say 3 
End Sub 

Public Sub AsyncProcess() 
    UpdateTextBox() 
End Sub 

Public Sub UpdateTextBox() 
    If Me.InvokeRequired Then 
     Me.Invoke(New MethodInvoker(AddressOf UpdateTextBox), "2") 
    Else 
     TextBox1.Text = "2" 
    End If 
End Sub 

End Class

Кто-нибудь знает, как мы можем ссылаться на что-то другое на главной форме нити, пока еще занят обработкой события LostFocus?

Заранее благодарен.

+0

Этот код блокирует без DoEvents, вызов Invoke() не может завершиться, пока поток пользовательского интерфейса вращается на IsCompleted. Использование BeginInvoke() вместо этого всегда будет отображать «2» после «3». Код слишком таинственный, чтобы предложить альтернативу. –

+0

Зачем беспокоиться о отображении 2 ?? 3 стирают его почти мгновенно. – GameAlchemist

+0

@ Vincent Piel - Я думаю, он просто пытается проиллюстрировать, что он хочет обновить пользовательский интерфейс из фонового потока, и он хочет, чтобы все происходило в определенном порядке. –

ответ

2

Ознакомьтесь с классом BackgroundWorker. Вы можете использовать этот класс для выполнения длительных задач в фоновом режиме и получения статуса отчета в пользовательском интерфейсе через события. Класс поддерживает событие «ProgressChanged» и событие «RunWorkerCompleted». Я привел пример кода ниже, чтобы показать, как использовать его для выполнения того, что вы пытаетесь сделать.

Imports System.ComponentModel 
Imports System.Threading 

Public Class Form1 

    Private Sub TextBox1_LostFocus(ByVal sender As Object, 
     ByVal e As System.EventArgs) Handles TextBox1.LostFocus 

     TextBox1.Text = "1" 

     ' Execute the long-running task in the background 
     BackgroundWorker1.RunWorkerAsync() 

    End Sub 

    Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, 
     ByVal e As DoWorkEventArgs) Handles BackgroundWorker1.DoWork 

     ' Do your heavy lifting in this method; report progress as needed by 
     ' calling ReportProgress, which will in turn call Progress_Changed 
     ' safely on the UI thread. (DO NOT update the UI directly from here.) 

     Dim worker As BackgroundWorker = CType(sender, BackgroundWorker) 

     ' Simulate processing for a second 
     Thread.Sleep(1000) 

     ' Report progress to the UI (the first arg is the percentage complete; 
     ' the secong arg can be a string or any object) 
     worker.ReportProgress(50, "2") 

     ' Simulate processing for another second 
     Thread.Sleep(1000) 

    End Sub 

    Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, 
     ByVal e As ProgressChangedEventArgs) 
     Handles BackgroundWorker1.ProgressChanged 

     ' Update the UI with progress from the background task 
     TextBox1.Text = CType(e.UserState, String) 

    End Sub 

    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, 
     ByVal e As RunWorkerCompletedEventArgs) 
     Handles BackgroundWorker1.RunWorkerCompleted 

     ' Update the UI when the background task is finished 
     TextBox1.Text = "3" 

    End Sub 
End Class 
+0

Не будет ли это точно так же, как делать BeginInvoke? Нет гарантии, что событие RunWorkerCompleted будет срабатывать, прежде чем менять текст на «3» ... поэтому окончательное значение будет «2», а не «3». –

+0

Нет, вам нужно будет сделать это немного измените свой код: выполните обработку «до» («1») в событии TextBox_LostFocus и запустите фоновый рабочий (как вы сейчас); сделайте «во время» обновлений («2») в событии ProgressChanged, затем, наконец, выполните любую «после» обработку («3») в событии RunWorkerCompleted. –

+0

Вышеупомянутое решение будет работать ... однако существующий проект довольно большой, если такое решение не будет осуществимо. Приведенный пример представляет собой очень упрощенную версию проблемы, чтобы проиллюстрировать проблемы. Идеальный поиск решения на основе очереди/события? –

0

Я думаю, что лучше всего будет не пытаться дождаться, когда метод завершится синхронно. Измените остальную часть метода обработчика LostFocus, которую вы хотите реализовать после вызова асинхронного вызова, в обратный вызов и передайте его на BeginInvoke. Это позволяет потоку пользовательского интерфейса оставаться отзывчивым при сохранении порядка операций.