Отзывчивость пользовательского интерфейса WinForms при работе с «тяжелыми» данными - PullRequest
5 голосов
/ 10 июля 2009

Я изменяю форму окна, чтобы позволить данным загружаться в фоновом режиме, пока пользовательский интерфейс остается отзывчивым. Данные занимают заметное время для извлечения и связывания. В идеале я хотел бы сделать оба в фоновом режиме, но есть некоторая неопределенность относительно того, какие обновления пользовательского интерфейса я должен делать в фоновом режиме (как вне основного потока). Прочный пример, который показывает получение данных и привязку данных в фоновом режиме, был бы очень полезен.

Ответы [ 3 ]

6 голосов
/ 10 июля 2009

Извлечение может и должно быть перенесено в фоновый поток - но есть некоторые шаблоны, чтобы поместить все это на место.

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

Существует три основных способа создания фоновых потоков для изучения

  • самый простой / наиболее ограниченный (и причудливый IMO) компонент BackgroundWorker
  • использование делегатов и их методов BeginInvoke () / EndInvoke () обеспечивают хороший баланс простоты и гибкости (и используют потоки ThreadPool)
  • использование необработанных объектов Thread обеспечивает максимальный контроль, но его настройка медленнее, чем ThreadPool Threads

Лично я склоняюсь к варианту «Делегаты»; с ними довольно легко работать, как только вы сломаете шаблон. BackgroundWorker выглядит неплохо, но в нем есть некоторые ошибки и недостающая сантехника, которые делают работу с ней более громоздкой, чем вы ожидаете. Позвольте мне привести краткий пример подхода делегата; Я скоро обновлю ...

редактировать

Вот некоторый код, он написан на VB, но должен быть достаточно легким для расшифровки, если вы парень на C #. У вас есть еще пара опций о том, как вы хотите, чтобы фоновый поток вел себя, поэтому здесь есть два примера. Я предпочитаю не блокировать, но если вы встраиваете его в существующий код, блокирование может быть проще для вас.

Неблокируемый метод обратного вызова (GetData_Complete) будет вызываться в потоке пользовательского интерфейса после завершения фонового потока

Sub Main()

    Console.WriteLine("On the main thread")
    Dim dataDelegate As New GetDataCaller(AddressOf GetData)

    Dim iar As IAsyncResult

    ' Non-blocking approach using a callback method
    iar = dataDelegate.BeginInvoke(AddressOf GetData_Complete, Nothing)

End Sub

Private Delegate Sub GetData_CompleteCaller(ByVal iar As IAsyncResult)
Private Sub GetData_Complete(ByVal iar As IAsyncResult)
    If InvokeRequired Then
        Dim invokeDelegate As New GetData_CompleteCaller(AddressOf GetData_Complete)
        Invoke(invokeDelegate, New Object() {iar})
        Exit Sub
    End If

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
    Dim result As String = dataDelegate.EndInvoke(iar)

    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
    Console.WriteLine("On the background thread!")

    For index As Integer = 0 To 2
        Console.WriteLine("Background thread is working")
    Next

    Return "Yay, background thread got the data!"

End Function

Блокировка Sub Main ()

    Console.WriteLine("On the main thread")
    Dim dataDelegate As New GetDataCaller(AddressOf GetData)

    Dim iar As IAsyncResult

    ' blocking approach; WaitOne() will block this thread from proceeding until the background thread is finished
    iar = dataDelegate.BeginInvoke(Nothing, Nothing)
    iar.AsyncWaitHandle.WaitOne()
    Dim result As String = dataDelegate.EndInvoke(iar)
    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Sub GetData_Complete(ByVal iar As IAsyncResult)

    ' Downcast the IAsyncResult to an AsyncResult -- it's safe and provides extra methods
    Dim ar As System.Runtime.Remoting.Messaging.AsyncResult = DirectCast(iar, System.Runtime.Remoting.Messaging.AsyncResult)

    Dim dataDelegate As GetDataCaller = DirectCast(ar.AsyncDelegate, GetDataCaller)
    Dim result As String = dataDelegate.EndInvoke(iar)

    Console.WriteLine("On the main thread again, background result is: " + result)

End Sub

Private Delegate Function GetDataCaller() As String
Private Function GetData() As String
    Console.WriteLine("On the background thread!")

    For index As Integer = 0 To 2
        Console.WriteLine("Background thread is working")
    Next

    Return "Yay, background thread got the data!"

End Function
1 голос
/ 10 июля 2009

Никогда не обновляйте пользовательский интерфейс из какого-либо фонового потока, после того как вы получили свои данные с сервера, вызовите его обратно в поток пользовательского интерфейса, чтобы обновить элементы управления или набор данных, с которыми связан ваш пользовательский интерфейс.

Использование BackgroundWorker поможет в этом случае просто связать события.

НТН

Phil '

0 голосов
/ 10 июля 2009

Загрузка (как в «извлечении из источника данных») может быть тривиальной, используете ли вы делегатов, фоновых рабочих или любой другой протокол. Но связывание кажется сложным, поскольку над ним не так много контроля, по крайней мере, в большинстве элементов управления с привязкой к данным - вы можете извлекать данные асинхронно, но как только вы будете готовы, как передать их в большую сетку в фоновом режиме? Это твой вопрос? Если это так, я думаю, вы можете:

  • создать (или подкласс) ваш элемент управления представлением, предоставляя интерфейсы для асинхронной загрузки;
  • реализует постраничное представление, показывающее только N записей одновременно, чтобы пользовательский интерфейс не блокировался при извлечении / форматировании записей.
...