views:

34

answers:

3

In my application, I have a MainWindow with a ToolStripProgressBar and a ToolStripStatusLabel.

This properties:

Property ProgressBarPercantage() As Integer Implements BCSXPSearchTool.Presenter.IMainView.ProgressPercentage
    Get
        Return Me._progressbarpercentage
    End Get
    Set(ByVal value As Integer)
        Me._progressbarpercentage = value
        Me.StatusStripCurrentProgressBar.Value = Me._progressbarpercentage
    End Set
End Property
Private _progressbarpercentage As Integer = 0

Property ProgressStatusText() As String Implements BCSXPSearchTool.Presenter.IMainView.ProgressStatusText
    Get
        Return Me._progressstatustext
    End Get
    Set(ByVal value As String)
        Me._progressstatustext = value
        Me.StatusStripCurrentState.Text = Me._progressstatustext
    End Set
End Property
Private _progressstatustext As String = "Ready"

In the MainWindowPresenter I start a new BackgroundWorker which should read from a database.

    Public Sub Search()
        Dim bw As New BackgroundWorker
        bw.WorkerReportsProgress = True
        bw.WorkerSupportsCancellation = True
        AddHandler bw.DoWork, AddressOf runproc
        If bw.IsBusy = False Then
            bw.RunWorkerAsync()
        End If
    End Sub


    Public Sub runproc()
        Dim statusToSub As delegateStatusTo = AddressOf statusTo
        Dim percToSub As delegatePercTo = AddressOf percTo
        statusToSub.Invoke("Test")
        'percToSub.Invoke(50)
    End Sub

    Public Sub percTo(ByVal value As Integer)
        _view.ProgressPercentage = value
    End Sub

    Public Sub statusTo(ByVal value As String)
        _view.ProgressStatusText = value
    End Sub

    Delegate Sub delegateStatusTo(ByVal value As String)
    Delegate Sub delegatePercTo(ByVal value As Integer)

The code above is working. But if I change the sub runproc() to:

    Public Sub runproc()
        Dim statusToSub As delegateStatusTo = AddressOf statusTo
        Dim percToSub As delegatePercTo = AddressOf percTo
        ' statusToSub.Invoke("Test")
        percToSub.Invoke(50)
    End Sub

It doesn't work. I get an exception:

InvalidOperationException

I got the text in english and can't translate it to english very well but I think something like:

The access to the control, created by another thread from another thread is not allowed.

I'm using Visual Studio 2008 Express + VB 2.0.

Thank you!

+1  A: 
Dim statusToSub As **new** delegateStatusTo(AddressOf WriteToDebug)
statusToSub.Invoke("Test")

Dim percToSub As **new** delegatePercTo (AddressOf percTo)
percToSub.Invoke(50)
vulkanino
If I call statusTo("Hello") in runproc() it works, but percTo(50) same problem.
elr
maybe the percTo sub is the problem...
vulkanino
Explicit instantiation of delegates is unnecessary, the conversion happens automatically.
Konrad Rudolph
+1  A: 

It looks like you are attempting to access UI controls from the DoWork event handler. Remember, that event handler is running on a worker thread. You are not allowed to touch any UI control from a thread other than the one that created it. There is a ProgressChanged event that will be marshaled onto the UI thread automatically upon calling ReportProgress. You safely update the UI from this event.

Brian Gideon
+1  A: 

This is due to cross-thread UI access which is disallowed (but for every UI access, so your other code shouldn’t work either!). The easiest solution is to use BeginInvoke when required:

Public Sub statusTo(ByVal value As String)
    If InvokeRequired Then
        BeginInvoke(New Action(Of String)(AddressOf statusTo))
        Return
    End If
    _view.ProgressStatusText = value
End Sub

Furthermore, @vulkanino’s comment is spot-on: your calls should be direct method calls, not delegate invocations.

Konrad Rudolph
Thank you, it works fine. But I' still confused why statusTo() works and percTo() doesn't work.
elr