Question ListBox problem - DisplayMember 'vanishes' when datasource updated

gchq

Well-known member
Joined
Dec 14, 2007
Messages
168
Programming Experience
10+
Hi there

I have a function that changes the text in the DisplayMember of a ListBox by first changing the DataTable DataSource and then reloading the datasource.

Trouble is, editing any value under the first item causes the DisplayMember values to 'vanish' from sight - they are still there as you can click on them, they just can't be viewed!

Any idea what might be happening?

VB.NET:
 RevenueDT.Select("ID = " & SelectedDTID)(0)("Name") = NewValue

                With RevenueListBox
                    .DataSource = Nothing
                    .DataSource = RevenueDT
                    .DisplayMember = "Name"
                    .ValueMember = "ID"
                    .SelectedIndex = SelectedDTIndex
                End With
 
First up, you should always set the DisplayMember and ValueMember before setting the DataSource.

Secondly, if ID is the PK of the table, don't use DataTable.Select but, rather, use DataTable.Rows.Find. It will return a single row by PK, so it's much more efficient than Select, which works for any column and therefore just does repeated equality comparisons on field values rather than hashing.

As for the question, that does sound strange. Are you saying that that same DataTable is already bound to the control, and you just want to display a different column? If so, there's no need to change the DataSource at all. You should bind your data via a BindingSource. You can then just change the DisplayMember and call ResetBindings. Try this example:
VB.NET:
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
    Dim table As New DataTable

    With table.Columns
        .Add("ID", GetType(Integer))
        .Add("FirstName", GetType(String))
        .Add("LastName", GetType(String))
    End With

    With table.Rows
        .Add(1, "Peter", "Smith")
        .Add(2, "Paul", "Jones")
        .Add(3, "Mary", "Williams")
    End With

    Me.BindingSource1.DataSource = table

    With Me.ListBox1
        .DisplayMember = "FirstName"
        .ValueMember = "ID"
        .DataSource = Me.BindingSource1
    End With
End Sub

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
    Me.ListBox1.DisplayMember = "LastName"
    Me.BindingSource1.ResetBindings(True)
End Sub
The SelectedItem is maintained, so there's no need for anything more.

If you try that and you still see anomalous behaviour then something is wrong with your system and I'd suggest repairing the Framework and maybe VS.
 
Solved

Thanks for your reply.

This was something of an odd one in that this section of the app kept having bits tacked on, and now this one report is over 11k Lines of Code..

The idea was that the user could edit an item in the list box..

Report_001.jpg


Report_002.jpg


and that is what the code I posted yesterday does...

Whilst debugging I found that not only does it run through an IndexChanged event

VB.NET:
 Private Sub CashFlowMainListBox_IndexChanged(ByVal MainLB As String, ByVal NominalLB As String, ByVal NomDT As DataTable)
        If AllowIndexChange = True Then <------This added to skip the event
            Dim LB As ListBox = RFC(ReportForm, MainLB)
            Dim NomLB As ListBox = RFC(ReportForm, NominalLB)
            If LB.SelectedValue.GetType.Name Is Nothing Then
                AppBox.Show("This was nothing", MessageBoxButtons.OK, MessageBoxIcon.Information)
                Exit Sub
            End If
            If LB.SelectedValue.GetType.Name <> "DataRowView" Then 'LB.Items.Count > 1 And LB.SelectedIndex <> -1 Then
                Dim CatID As Integer = LB.SelectedValue
                Dim DT As DataTable = NomDT.Copy()
                Dim vDV As New DataView(DT)
                vDV.RowFilter = "CatID = " & CatID & " AND FormID = " & FormID
                vDV.Sort = "Position"
                DT = vDV.ToTable
                vDV = Nothing
                With NomLB
                    .DataSource = Nothing
                    .DataSource = DT
                    .ValueMember = "ID"
                    .DisplayMember = "NomName"
                    .Refresh()
                End With
                LB.Refresh()
            End If
              End Sub


it also runs though a DrawItemEvent

VB.NET:
Private Sub listBox_DrawItem(ByVal sender As Object, ByVal e As DrawItemEventArgs)
        e.DrawBackground()
        Dim myBrush As Brush = Brushes.Black
        For Each row As DataRow In RevenueNomDT.Rows
            Dim vNeg As Integer = row("NegValue")
            Dim vPos As Integer = 99999
            If vNeg = 1 Then
                vPos = row("Position")
            End If
            Dim vSelected As Integer = CType(sender, ListBox).SelectedIndex
            Select Case e.Index
                Case vPos
                    myBrush = Brushes.Red
            End Select
            If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                myBrush = Brushes.White  <----- This was causing the problem
            End If
            e.Graphics.DrawString(CType(sender, ListBox).Items(e.Index).Item("NomName"), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault)
            e.DrawFocusRectangle()
        Next
    End Sub

..and repainting the forecolour white - explains why I couldn't see it, but could click on it.

So I added this to the code:-

VB.NET:
Case 5
                AllowIndexChange = False  <---- added this
                RevenueDT.Select("ID = " & SelectedDTID)(0)("Name") = NewValue
                With RevenueListBox
                    .DataSource = Nothing
                    .DataSource = RevenueDT
                    .DisplayMember = "Name"
                    .ValueMember = "ID"
                    '.SelectedIndex = SelectedDTIndex
                End With
                AllowIndexChange = True <---- added this

and it now runs...

I will look at the other suggestions you made and change those as well

Thanks again for taking the time to reply
 
Back
Top