Страница: 1 | 2 |
Вопрос: Обновление интерфейса из другого потока
Добавлено: 22.02.08 07:45
Автор вопроса: Legon
Доброго времени суток! У меня возникла проблема: Приложение занимается копированием файлов. И делает это в другом потоке. Вопрос: Как из Второго потока(через делегаты, события или "заклинания" обновлять содержимое ProgressBar'a и Label'ов?
Я создал отдельный класс, через который передал другому потоку параметры для его запуска (список файлов для копирования и т.д.). В классе создал события, которые возникают при завершении копирования каждого файла, и эти события работают!... но в другом потоке.
Как мне обновлять ПрогрессБар по завершении копирования файла??? Или как мне инициализировать событие из кода второго процесса, а обработчик создать в основном?
Заранее спасибо!
Ответы
Всего ответов: 19
Номер ответа: 1
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #1
Добавлено: 22.02.08 10:38
Изменить свойства элементов можно только в методе потока, в котором этот элемент был создан! что бы вызвать этот метод, надо использовать делегаты.
‘делегат
Private Delegate Sub DelegateChangeProperty
‘поток из которого надо изменить свойства
Private Sub Thread_1
Dim myDelegate As New SetClipboardText(AddressOf ChangeProperty)
‘Вызов метода в основном потоке
myDelegate.Invoke()
End sub
‘Метод в основном потоке
Privet Sub ChangeProperty
‘Меняем свойства элементов
End sub
Номер ответа: 2
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #2
Добавлено: 22.02.08 10:40
P.S. - передачу параметров никто не отменял
Номер ответа: 3
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #3
Добавлено: 22.02.08 17:54
Ничего не получается.
И вообще, я немного не понял строку
Dim myDelegate As New SetClipboardText(AddressOf ChangeProperty)
Привожу упрощенный пример своего программного кода:
Imports System.IO
Public Class frmStatus
Public Event FileDone()
Public WithEvents FO As New FileOperations
Dim Thrd As New Thread(AddressOf FO.Copy) 'Запуск процедуры в классе
Public tPath As String
Private Sub frmStatus_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Show()
Me.Refresh()
StartOperation()
End Sub
Public Sub StartOperation()
Dim i As Integer
With frmMain.LV
For i = 0 To .Items.Count - 1
'все проверки и передача значений через класс в другой поток
'......
FO.FileList.Add(tFile)
Next
End With
'передача пути
FO.tPath = tPath
'Запуск метода класса в другом потоке
Thrd.Start()
End Sub
Private Sub FO_Done() Handles FO.Done
 ialogResult = Windows.Forms.DialogResult.OK
End Sub
Class FileOperations
Public Class LFile
Dim _Name As String
Dim _Path As String
Public Property Name()
'...
End Property
Public Property Path()
'...
End Property
End Class
'Передаем в другой поток через класс параметры
Public FileList As New ArrayList
Public tPath As String
'Читаем из потока через класс значения
Public curFile As String
Public curPercent As Double
Public Event Done()
Public Sub Copy()
Dim MyDelegatePB As New DelegatePBChangeValue(AddressOf frmStatus.PBChangeValue)
Dim i As Long
Dim tPer As Double
Dim cPer As Double
Dim tFile As LFile
cPer = 100 / FileList.Count
For i = 0 To FileList.Count - 1
tFile = CType(FileList(i), LFile)
curFile = tFile.Name
'нужно изменить значения лэйблов
File.Copy(tFile.Path, Path.Combine(tPath, tFile.Name))
tPer += cPer
curPercent = tPer
MyDelegatePB.Invoke()
'нужно изменить значение прогрессбара
Next
RaiseEvent Done()
End Sub
End Class
End Class
Помогите дописать код. В местах "нужно изменить значение..." нужно взаимодействовать с интерфейсом.
Номер ответа: 4
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #4
Добавлено: 22.02.08 17:57
З.Ы. На строку
Номер ответа: 5
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #5
Добавлено: 22.02.08 18:11
извиняюсь "im myDelegate As New SetClipboardText(AddressOf ChangeProperty)" - остатки моего кода
в моем примере вместо SetClipboardText, надо использовать DelegateChangeProperty
Номер ответа: 6
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #6
Добавлено: 22.02.08 18:26
Imports System.Threading
Imports System.IO
Public Class frmStatus
Public Event FileDone()
Public WithEvents FO As New FileOperations
 im Thrd As New Thread(AddressOf FO.Copy) 'Запуск процедуры в классе
Public tPath As String
Private Sub frmStatus_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.Show()
Me.Refresh()
StartOperation()
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub PBChangeValue(ByVal curFile As String, ByVal curPercent As Double)
' туд делаем что то с прогрессбаром и т.д.
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Sub StartOperation()
 im i As Integer
With frmMain.LV
For i = 0 To .Items.Count - 1
'все проверки и передача значений через класс в другой поток
'......
FO.FileList.Add(tFile)
Next
End With
'передача пути
FO.tPath = tPath
'Запуск метода класса в другом потоке
Thrd.Start()
End Sub
Private Sub FO_Done() Handles FO.Done
 ialogResult = Windows.Forms.DialogResult.OK
End Sub
Class FileOperations
Public Class LFile
 im _Name As String
 im _Path As String
Public Property Name()
'...
End Property
Public Property Path()
'...
End Property
End Class
'Передаем в другой поток через класс параметры
Public FileList As New ArrayList
Public tPath As String
'Читаем из потока через класс значения
Public curFile As String
Public curPercent As Double
Public Event Done()
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Delegate Sub DelegatePBChangeValue(ByVal curFile As String, ByVal curPercent As Double)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Sub Copy()
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 im MyDelegatePB As New DelegatePBChangeValue(AddressOf сылка_на_форму.PBChangeValue)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
 im i As Long
 im tPer As Double
 im cPer As Double
 im tFile As LFile
cPer = 100 / FileList.Count
For i = 0 To FileList.Count - 1
tFile = CType(FileList(i), LFile)
curFile = tFile.Name
'нужно изменить значения лэйблов
File.Copy(tFile.Path, Path.Combine(tPath, tFile.Name))
tPer += cPer
curPercent = tPer
''''''''''''''''''''''''''''''''''''''''''''''
MyDelegatePB.Invoke(curPercent, curFile)
''''''''''''''''''''''''''''''''''''''''''''''
'нужно изменить значение прогрессбара
Next
RaiseEvent Done()
End Sub
End Class
End Class
Номер ответа: 7
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #7
Добавлено: 22.02.08 19:35
Спасибо большое! Помогло!
Осталось только одно но: Данные передаются нормально. А вот обновляется не моя форма, которая на экране, а ее инстанс. Причем откуда он создался для меня загадка. Буду бороться дальше.
З.Ы. Главная форма открывает диалоговую. Диалоговая при загрузке вызывает подрограмму, которая запускает поток. А вот обновляется не форма, а инстанс. Может поможете переиграть эту нерабочую схему?
Номер ответа: 8
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #8
Добавлено: 22.02.08 20:31
И каким образом возник этот инстанс?
Номер ответа: 9
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #9
Добавлено: 23.02.08 03:16
Просидев еще несколько часов, я пришел к выводу: Данные из другого потока действительно передаются в обработчик. И обработчик действительно меняет свойства контролов. И прочитать я могу эти значения. Но они не обновляются в модальном окне. А если вызвать метод Show, то появляется другое окно, в котором и происходят обновления. Но это окно ведет себя так, будто оно находится во втором потоке. И в этом же обработчике я не могу изменить свойства контролов главного окна. Вернее могу, но изменения касаются "другого" главного окна. "Паралельного".
Надеюсь, последний вопрос:
Как грамотно (в какой последовательности) сделать следующее (из главной формы):
1. Передать потоку входящие параметры (скорее всего через класс, но может есть другие способы)
2. Открыть модальное окно статуса выполнения. (которое заблокирует основное до окончания действия)
3. Запустить второй поток.
4. В момент выполнения второго потока обновлять свойства контролов модального окна.
5. По завершении потока сообщить об этом основному потоку.
Я знаю, что звучит это все довольно банально, но у меня почему-то не работает. И я не знаю где ошибка: в способе открытия модального окна, в вызове второго потока, в способе общения между потоками или ... в моем ДНК (шутка ) Но оно ведь должно работать???!?!?
Если кто-то еще читает этот пост, пАмАгите пожалуйста. Очень хочу освоить многопоточность. В книгах есть масса примеров, и ни одного по моему вопросу.
Номер ответа: 10
Автор ответа:
ника
Вопросов: 1
Ответов: 111
Профиль | | #10
Добавлено: 23.02.08 08:57
Могу набросать пример, но на С#(ввиду отсутствия VB).. осилишь?
Номер ответа: 11
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #11
Добавлено: 23.02.08 10:48
я бы посоветовал использовать интерфейс BackgroundWorker, в нем реализованы события запуск , изменение в потоке, завершение потока!
В данном примере я бы поступил так:
1) myThread.Start(параметры), параметры - можно описать в виде структуры или класса, что собственно не ограничивает набор параметров
2) запустить дополнительный поток
3) показать модальное окно, которое будет отображать ход выполнения (ShowDialog - метод, который запускает именно в модальном режиме)
4) c помощью делегата изменять свойства диалога
5) по окончанию работы доп. потока скрыть и освободить диалог
Номер ответа: 12
Автор ответа:
fluke
ICQ: 318170731
Вопросов: 15
Ответов: 96
Профиль | | #12
Добавлено: 23.02.08 10:52
A) System.ComponentModel.BackgroundWorker (методы другие, но план выполнения такой же как и для System.Threading.Thread)
Б) План действия расписан для System.Threading.Thread
Номер ответа: 13
Автор ответа:
Artyom
Разработчик
Вопросов: 130
Ответов: 6602
Профиль | | #13
Добавлено: 23.02.08 19:16
По поводу фантомной формы могу пояснить в чем проблема
В принципе интерисует этот кусок кода, какая именно ссылка передается?
Чтоб фантомное окно не создавалось, заведи глобальную ссылку на свой экземпляр формы.
Номер ответа: 14
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #14
Добавлено: 24.02.08 09:15
Сорри, за задержку с ответом. (Был на работе и только утром добрался до компа. оффтоп)
2 Steel Brand: К сожалению я пытался и через глобальную переменную, и по прямой ссылке запускать форму и ссылаться на нее. Результат одинаковый - неработает.
2 fluke: Спасибо за совет. Сегодня займусь реализацией. К вечеру отпишусь о результатах.
Номер ответа: 15
Автор ответа:
Legon
Вопросов: 4
Ответов: 32
Профиль | | #15
Добавлено: 24.02.08 09:16
Сорри, за задержку с ответом. (Был на работе и только утром добрался до компа. оффтоп)
2 Steel Brand: К сожалению я пытался и через глобальную переменную, и по прямой ссылке запускать форму и ссылаться на нее. Результат одинаковый - неработает.
2 fluke: Спасибо за совет. Сегодня займусь реализацией. К вечеру отпишусь о результатах.