Platypus
Platypus

Reputation: 61

DatagridView crashes App when updating causes scrollbars to appear

My vb.net 4.0 project is in-house trading platform.

A separate form called MsgPad contains a Datagridview called MsgPadDG.

The Form-load sub does two things: formats the datagridview and defines a timer for the gridview refresh.

A second Sub, UpdateMyMsg, receives updates from a threaded UIUpdaterEngine by means of NewMessage events, and updates the MyMessages Object (fundamentally a datatable).

A third sub, UpdateMdgPadDGW, updates/refreshes the datagridview using the invoke method (probably not necessary, because the timer is defined in the UI thread).

Finally The Datagridview properties: rows are defined as not user-editable, AutoSizeRowsMode = Displayedcells and ScrollBars = Both.

My problem is that the entire App crashes when the updating process adds rows beyond the limit of the datagridview, causing scrollbars to appear. I tried without the timer (invoking datasource update directly in the UpdateMyMsg Sub), with the timer, but the problem is always there. Weird thing: The Try-Catch block doesn't catch anything.

Thanks in advance for your replays.

Edoardo

HERE COMES THE CODE:

Public Class MsgPad
Private WithEvents _MyUIUpdaterEngine As MyUIUpdaterEngine = MyUIUpdaterEngine.Instance
Private MyMessages As New Messages
Private LastUpdate As DateTime = TimeValue("08:00:00")
Private ObjLock As New Object
Private NewMessages As Boolean = False
Dim UpdateDGWTimer As New System.Timers.Timer

 Private Sub MsgPad_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    Try
        '--
        MsgPadDG.DataSource = MyMessages.DTable
        '--
        With MsgPadDG
            '--
            .Columns("Msg_ID").Visible = False
            .Columns("I_CommandID").Visible = False
            .Columns("B_ID").Visible = False
            .Columns("F_ID").Visible = False
            .Columns("T_TV").Visible = False
            .Columns("T_Sign").Visible = False
            .Columns("T_Mde").Visible = False
            .Columns("T_QU").Visible = False
            .Columns("T_QI").Visible = False
            .Columns("T_QF").Visible = False
            .Columns("T_PI").Visible = False
            .Columns("T_PF").Visible = False
            '--
            .Columns("Msg_RecDate").HeaderText = "Date"
            .Columns("Msg_RecDate").Width = 50
            .Columns("Msg_RecDate").DefaultCellStyle.Format = "HH:mm:ss"
            .Columns("I_Symbol").HeaderText = "Symbol"
            .Columns("I_Symbol").Width = 80
            .Columns("I_Des").HeaderText = "Des"
            .Columns("I_Des").Width = 180
            .Columns("Msg_Text").HeaderText = "Message"
            .Columns("Msg_Text").Width = 250
            '--

        End With

        '--Crea timer per l'Update del DAtagridView
        AddHandler UpdateDGWTimer.Elapsed, AddressOf UpdateMsgPadDGW
        UpdateDGWTimer.Interval = 3500
        UpdateDGWTimer.Enabled = True
 .....
 .....
 End Sub

 Private Sub UpdateMyMsg(ByVal CRec As OrderRecord, ByVal Msg As String) Handles _MyUIUpdaterEngine.NewMessage

    Try
        SyncLock ObjLock
            '--Aggiorna la tabella MyMessages
            MyMessages.Add(CRec, Msg)
            NewMessages = True
            '--Parla messaggio
            Dim synth As New SpeechSynthesizer
            Dim TalkMsg As String = Msg
            If CRec.I_Des <> Nothing Then TalkMsg += " su: " + CRec.I_Des
            synth.SpeakAsync(TalkMsg)
        End SyncLock
 .....
 .....
 End Sub


 Private Sub UpdateMsgPadDGW(source As Object, e As ElapsedEventArgs)

    '--Aggiorna MesssagePad
    Try
        SyncLock ObjLock
            If NewMessages Then
                MsgPadDG.ControlInvoke(Sub(l)
                                           MsgPadDG.DataSource = MyMessages.DTable
                                           MsgPadDG.Refresh()
                                           MsgPadDG.ClearSelection()
                                       End Sub)
                NewMessages = False
            End If
        End SyncLock
 .....
 .....
 End Sub

Upvotes: 1

Views: 1116

Answers (1)

Hans Passant
Hans Passant

Reputation: 941545

        '--Aggiorna la tabella MyMessages
        MyMessages.Add(CRec, Msg)
        NewMessages = True

That's a fatal bug, the DGV is bound to MyMessages. So calling Add() in a worker thread causes the control to be updated from the wrong thread. You normally get an IllegalOperationException from accessing a control from the wrong thread but that unfortunately doesn't work for bound data.

You'll need to do this differently, have the worker add messages to a List instead. Then in the invoked code update MyMessages with these new messages.

Also note that your Timer is not a safe timer like you assumed in your question. Only a System.Windows.Forms.Timer is a safe one whose Tick event handler will run on the UI thread. You then also don't need to invoke anymore.

Upvotes: 1

Related Questions