2014-11-08 3 views
1

장시간 독자, 처음 포스터. 보통 나는 대답을 찾아서 작동시킬 수 있습니다. 이번에는 ..... 저는 VS2013에서 VB.NET을 사용하고 있습니다. 보조 스레드에서 수행 한 작업으로 진행률 표시 줄을 업데이트하려고합니다. 알았어? 아니, 더 복잡하게 만들어야 했어. 진행률 표시 줄 (ToolStripProgressBar1)은 프로젝트의 MDI 인 기본 폼 (frmMain)에 있습니다. 보조 양식 (frmShipping)에는 클래스의 일부 COMM 포트 통신을 수행하는 두 번째 스레드를 시작하는 버튼이 있습니다 (cApex). 업데이트 진행률 막대를 frmMain 메인 UI 스레드 (frmShipping 버튼)에서 가져올 수 있습니다. 모든 위대한 작품을vb.net 업데이트 진행률 표시 줄 다중 스레드

Private Sub btnreadScanner_Click(sender As Object, e As EventArgs) Handles btnreadScanner.Click 

    Dim thrReadScanner As New System.Threading.Thread(AddressOf ReadScanner) 
    thrReadScanner.IsBackground = True 
    thrReadScanner.Start() 

End Sub 

    Private Sub ReadScanner() 

    Dim strRowCount As String 
    ShipmentMsg(2) 
    strRowCount = objShipping.RecordsExisit.ToString() 

    Try 
     objApex.ImmediateMode() 
     If objApex.FileDownload = False Then 
      Throw New Exception(Err.Description) 
     End If 
    Catch ex As Exception 
     ShipmentMsg(1) 
     MessageBox.Show("No Data downloaded from Scanner. Try Again. Error#: " & Err.Number & " : " & Err.Description) 
     Exit Sub 
    End Try 

    RecordCount() 
    DataGridUpdate() 
    btnProcessShipment.Enabled = True 
    ShipmentMsg(5) 
    ScanErrors() 

End Sub 

이 :

frmShiping 버튼의 코드와 멀티 스레드 절차입니다. 예상대로. objApex.FileDownloadcApex의 호출은 진행률 표시 줄을 (실제로 FileDownload에서 호출 한 다른 함수에서) 업데이트 할 위치입니다. 여기에 코드가 있습니다.

Try 
     GetHeaderRecord() 
     If Count <> 0 Then intTicks = Math.Round((100/Count), 1) 
     For intcount As Integer = 1 To Count 
      Dim intLength As Integer = Length 
      Do While intLength > 0 
       literal = Chr(_serialPort.ReadChar.ToString) 
       If literal = ">" Then Exit Do 
       strRecord = strRecord & literal 
       intLength = intLength - 1 
      Loop 
      REF = strRecord.Substring(0, 16).TrimEnd 
      SKID = strRecord.Substring(16, 16).TrimEnd 
      REEL_BC = strRecord.Substring(32, 15).TrimEnd 
      ScanDate = strRecord.Substring(47, 8).TrimEnd 
      ScanDate = DateTime.ParseExact(ScanDate, "yyyyMMdd", Nothing).ToString("MM/dd/yyyy") 
      ScanTime = strRecord.Substring(55, 6).TrimEnd 
      ScanTime = DateTime.ParseExact(ScanTime, "HHmmss", Nothing).ToString("HH:mm:ss") 
      strRecordTotal = strRecordTotal & strRecord & CRLF 
      Dim strSQL As String 
      strSQL = "INSERT INTO tblScanData (PONo,Barcode,SkidNo,ScanDate,ScanTime) " & _ 
      "VALUES (" & _ 
      Chr(39) & REF & Chr(39) & _ 
      "," & Chr(39) & REEL_BC & Chr(39) & _ 
      "," & Chr(39) & SKID & Chr(39) & _ 
      "," & Chr(39) & ScanDate & Chr(39) & _ 
      "," & Chr(39) & ScanTime & Chr(39) & ")" 
      objData.Executecommand(strSQL) 
      strRecord = "" 
     Next 

마지막으로 진행률 표시 줄을 업데이트하는 방법입니다.

Dim f As frmMain = frmMain 
System.Threading.Thread.Sleep(100) 
DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep() 

정말 PerformStep을 For 루프에 넣어야합니다. 루프를 돌 때마다 진행률 막대를 단계별로 계산하여 막대를 공정하게 정확하게 만드는 데 필요한 단계의 비율을 계산합니다 (루프 이전의 수학 코드로 완료). 또한 진행률 표시 줄 컨트롤의 속성을 frmMain에 설정했습니다. 그래서 나는 미쳐 있습니까? 아니면 이것을 성취 할 수있는 방법이 있습니까? 나는 위임자를 사용하여 시도했다. Me.Invoke (New MethodInvoker (AddressOf pbStep)) 코드를 스레드로부터 안전하게 보호합니다. 크로스 스레드 호출에 대해서는 오류가 발생하지 않지만 진행률 표시 줄은 업데이트되지 않습니다. 미안하지만 길다.하지만 나는 길을 잃고 내 ADHD는이 아이디어를 폐기하지 못하게 할 것이다.

편집은 AS 요청한 :

Public Sub pbStep() 

    Dim f As frmMain = frmMain 
    If Me.InvokeRequired Then 
     Me.Invoke(New MethodInvoker(AddressOf pbStep)) 
    Else 
     DirectCast(f, frmMain).ToolStripProgressBar1.PerformStep() 
     System.Threading.Thread.Sleep(100) 
    End If 

End Sub 
+0

코드의 어느 부분이'pbStep'입니까? 아직 표시되지 않은 경우 표시해야합니다. – TyCobb

+0

이것은 표준 VB.NET 트랩입니다. frmMain은 개체 참조가 아니라 형식 이름입니다. 스레드에서 사용하면 frmMain의 * new * 인스턴스가 생깁니다. 표시되지 않습니다. Show() 메서드는 호출되지 않았습니다. 반창고는 'Dim f'를 Else 절로 이동시키는 것입니다. 100msec 동안 잠자는 것은 거의 이해가되지 않습니다. 진행률을 계산할 수 없다면 진행률 막대를 Style = Marquee와 함께 사용하십시오. BackgroundWorker를 사용하면 이러한 트랩을 피할 수 있습니다. –

+0

한스 - 귀하의 통찰력이 정확했습니다. 'f.Show'는 폼에 포커스를두고 진행 막대를 보았고 예상대로 수행했습니다. 진실은 이것이 내가 어떻게 작동하기를 원하지 않는가라는 것입니다. 내가 속일 수있는 걸 보게 될거야. 아래 James 제안은 BackgroundWorker도 사용합니다. BGW를 사용하기 위해 코드를 다시 작성하는 것에 반대하지는 않을 것입니다. 그러나 나는 그들에 대한 것이고, 그들이 내 상황에서 어떻게 작동 할 것인지 모릅니다. 위의 코드에 대한 제안 사항 (코드 작성을 요구하는 것이 아니므로 지시 사항을 이해하는 데 도움이됩니다). – Mckenzie

답변

0

배경 노동자는이 목적을 위해 유용합니다. 그냥 대리인과 함께 사용하십시오. 모든 스레드 작업은 작업자의 DoWork 이벤트 내에서 수행됩니다. 진행이 진행되면 DoWork 이벤트에서 진행 상태가보고됩니다. 그러면 진행률 막대와 동일한 스레드에있는 작업자 클래스의 ProgressedChanged 이벤트가 발생합니다. DoWork가 완료되고 범위를 벗어나면 RunWorkerCompleted 이벤트가 발생합니다. 이것은 작업이 완료되었다는 것을 사용자에게 알려주는 데 사용할 수 있습니다. 여기에 함께 던진 작업 솔루션이 있습니다. 그냥 빈 양식 뒤에 붙여서 실행하십시오.

Imports System.Windows.Forms 
Imports System.ComponentModel 
Imports System.Threading 

Public Class Form1 
    Private _progressBar As ProgressBar 
    Private _worker As BackgroundWorker 

    Sub New() 
     ' This call is required by the designer. 
     InitializeComponent() 
     Initialize() 
     BindComponent() 
    End Sub 

    Private Sub Initialize() 
     _progressBar = New ProgressBar() 
     _progressBar.Dock = DockStyle.Fill 

     _worker = New BackgroundWorker() 
     _worker.WorkerReportsProgress = True 
     _worker.WorkerSupportsCancellation = True 

     Me.Controls.Add(_progressBar) 
    End Sub 

    Private Sub BindComponent() 
     AddHandler _worker.ProgressChanged, AddressOf _worker_ProgressChanged 
     AddHandler _worker.RunWorkerCompleted, AddressOf _worker_RunWorkerCompleted 
     AddHandler _worker.DoWork, AddressOf _worker_DoWork 
     AddHandler Me.Load, AddressOf Form1_Load 
    End Sub 

    Private Sub Form1_Load() 
     _worker.RunWorkerAsync() 
    End Sub 

    Private Sub _worker_ProgressChanged(ByVal o As Object, ByVal e As ProgressChangedEventArgs) 
     _progressBar.Increment(e.ProgressPercentage) 
    End Sub 

    Private Sub _worker_RunWorkerCompleted(ByVal o As Object, ByVal e As RunWorkerCompletedEventArgs) 

    End Sub 

    Private Sub _worker_DoWork(ByVal o As Object, ByVal e As DoWorkEventArgs) 
     Dim worker = DirectCast(o, BackgroundWorker) 

     Dim value = 10000 

     SetProgressMaximum(value) 

     For x As Integer = 0 To value 
      Thread.Sleep(100) 
      worker.ReportProgress(x) 
     Next 
    End Sub 

    Private Sub SetProgressMaximum(ByVal max As Integer) 
     If _progressBar.InvokeRequired Then 
      _progressBar.Invoke(Sub() SetProgressMaximum(max)) 
     Else 
      _progressBar.Maximum = max 
     End If 
    End Sub 

End Class 
+0

팁 주셔서 감사. 백그라운드 작업자를 살펴 보았지만이를 현재 코드에 통합하는 방법을 알 수 없었습니다. 나는 오늘 저녁에 이것을 시험해보고 내가 생각해내는 것을 당신에게 알려줄 것입니다. 빠른 답변 감사합니다. – Mckenzie

+0

진행률 표시 줄을 어떻게 만들 었는지는 알지만 그 상황에서 어떻게 작동합니까? 내 "직장"은 어디로 갈 것인가? 그리고 'For 루프'가 바를 진행하기 위해 - 어떻게하면 내 작업의 진행 상황을 정확하게 나타낼 수 있을까요? 나는이 BackgroundWorkers 프로세스에 대해 단서가 없다. 나는 읽고 이해하려고 노력하고 있습니다. – Mckenzie

2

두 응답을 통해 올바른 답을 얻을 수있었습니다. James가 제공 한 코드는 빌드의 시작점이었으며 Hans는 BackgroundWorker를 설명하는 여러 게시물을 가지고 있습니다. 내가 생각해 낸 "답변"을 나누고 싶었습니다. 나는 이것을하는 가장 좋은 방법을 말하는 것이 아니며, 나는 공통 논리의 몇 가지 규칙을 어기는 것이라고 확신한다. 또한 많은 코드는 MSDN 예제와 James 코드에서 가져온 것입니다.

먼저 bgw, frmShipping을 호출하는 양식부터 시작하십시오.이 코드를 추가했습니다 :

Dim WithEvents bgw1 As New System.ComponentModel.BackgroundWorker 

Private Sub bgw1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _ 
    Handles bgw1.RunWorkerCompleted 

    If e.Error IsNot Nothing Then 
     MessageBox.Show("Error: " & e.Error.Message) 
    ElseIf e.Cancelled Then 
     MessageBox.Show("Process Canceled.") 
    Else 
     MessageBox.Show("Finished Process.") 
    End If 

End Sub 

Private Sub bgw1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) _ 
    Handles bgw1.ProgressChanged 
    DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Maximum = 1960 
    DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.Step = 2 

    Dim state As cApex.CurrentState = 
     CType(e.UserState, cApex.CurrentState) 
    DirectCast(Me.MdiParent, frmMain).txtCount.Text = state.LinesCounted.ToString 
    DirectCast(Me.MdiParent, frmMain).txtPercent.Text = e.ProgressPercentage.ToString 
    DirectCast(Me.MdiParent, frmMain).ToolStripProgressBar1.PerformStep() 

End Sub 
Private Sub bgw1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) _ 
    Handles bgw1.DoWork 

    Dim worker As System.ComponentModel.BackgroundWorker 
    worker = CType(sender, System.ComponentModel.BackgroundWorker) 

    Dim objApex As cApex = CType(e.Argument, cApex) 
    objApex.CountLines(worker, e) 

End Sub 

Sub StartThread() 

    Me.txtCount.Text = "0" 
    Dim objApex As New cApex 
    bgw1.WorkerReportsProgress = True 
    bgw1.RunWorkerAsync(objApex) 

End Sub 

다음 코드에 my cApex 클래스를 추가했습니다.

Public Class CurrentState 
    Public LinesCounted 
End Class 

Private LinesCounted As Integer = 0 


Public Sub CountLines(ByVal worker As System.ComponentModel.BackgroundWorker, _ 
         ByVal e As System.ComponentModel.DoWorkEventArgs) 
    Dim state As New CurrentState 
    Dim line = "" 
    Dim elaspedTime = 20 
    Dim lastReportDateTime = Now 
    Dim lineCount = File.ReadAllLines(My.Settings.strGenFilePath).Length 
    Dim percent = Math.Round(100/lineCount, 2) 
    Dim totaldone As Double = 2 

    Using myStream As New StreamReader(My.Settings.strGenFilePath) 

     Do While Not myStream.EndOfStream 
      If worker.CancellationPending Then 
       e.Cancel = True 
       Exit Do 
      Else 
       line = myStream.ReadLine 
       LinesCounted += 1 
       totaldone += percent 

       If Now > lastReportDateTime.AddMilliseconds(elaspedTime) Then 
        state.LinesCounted = LinesCounted 
        worker.ReportProgress(totaldone, state) 
        lastReportDateTime = Now 
       End If 
       System.Threading.Thread.Sleep(2) 
      End If 
     Loop 

     state.LinesCounted = LinesCounted 
     worker.ReportProgress(totaldone, state) 

    End Using 

End Sub 

은 또한 읽고 내 단추의 Click 이벤트에 나서 100의 비율로 전체 진행되는 파일에서 현재 행 수를 표시하는 내 기본 폼에 텍스트 상자의 몇 가지를 추가 방금 StartThread()으로 전화하면됩니다. 100 % 정확하지는 않지만 사용자가 프로세스가 어떤 위치에 있는지 아주 잘 알 수있을만큼 근접합니다. 원래는 진행률 표시 줄을 사용하고자하는 "ReadScanner"기능에 추가 할 작업이 조금 있습니다. 하지만이 기능은 스캐너에서 수행하는 두 가지 작업 중 더 길어 COMM 포트를 통해 거의 2,000 줄의 코드를 작성합니다. 결과에 만족합니다.

도와 주신 여러분 께 감사드립니다.

P. 또한 이제 pbar.Maximumpbar.step을 설정하는 변수를 추가 했으므로 스캐너 파일이 변경되면 변수가 변경 될 수 있습니다.

+0

즐거움은 내 것입니다! 행운을 빕니다! –

관련 문제