Search code examples
c#.netwinformsms-accessgraphics

ListBox items do not show in OwnerDrawFixed Mode


Please, I really appreciate that anyone can help me with the following problem.

I have an Access database which I want to load it into the ListBox.
I set the ListBox DrawMode to OwnerDrawFixed, but when I run the application it shows (System.Data.DataRowView) instead of all the database records.

I must say that it works fine in Normal DrawMode.

ListBox OwnerDrawFixe Mode Problem Image

Here is my code:

private void Form1_Load(object sender, EventArgs e)
    {
        ListBox1.DataSource = GetData();
        ListBox1.DisplayMember = "empName";
    }

DataTable dt;

private DataTable GetData()
    {
        dt = new DataTable();
        using (OleDbConnection myConn = new OleDbConnection(ConfigurationManager.ConnectionStrings["database"].ConnectionString))
        {
            using (OleDbCommand myQuery = new OleDbCommand("select empName from empTable", myConn))
            {
                myConn.Open();
                OleDbDataReader myReader = myQuery.ExecuteReader();
                dt.Load(myReader);
            }
        }
        return dt;
    }


private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
    {
        e.DrawBackground();

        bool isItemSelected = ((e.State & DrawItemState.Selected) == DrawItemState.Selected);
        int itemIndex = e.Index;
        if (itemIndex >= 0 && itemIndex < ListBox1.Items.Count)
        {
            Graphics g = e.Graphics;

            SolidBrush backgroundColorBrush = new SolidBrush((isItemSelected) ? Color.FromArgb(255, 64, 64, 64) : Color.FromArgb(0,64,64,64)); 
            g.FillRectangle(backgroundColorBrush, e.Bounds);

            // Set text color
            string itemText = ListBox1.Items[itemIndex].ToString();

            SolidBrush itemTextColorBrush = (isItemSelected) ? new SolidBrush(Color.White) : new SolidBrush(Color.LightGray); 
            g.DrawString(itemText, e.Font, itemTextColorBrush, ListBox1.GetItemRectangle(itemIndex).Location);

            // Clean up
            backgroundColorBrush.Dispose();
            itemTextColorBrush.Dispose();
        }
        e.DrawFocusRectangle();
    }

Thank you again.


Solution

  • I suggest these changes in relation to the way the data is loaded and used to fill the Items collection of a ListBox and the way the DrawItems method performs the painting of the items:

    1. Use a OleDbDataAdapter to fill the DataTable instead of a OleDbDataReader. It's simple to use and it takes care of opening the connection for you.
    2. Return a DataTable, then use the first Column's name as the ListBox DisplayMember, so you don't need to hardcode (here) the name of the Field that provides the information to show. You'll have this reference in one place only (less error prone).
    3. Remove the unnecessary parts in the DrawItem method and make use of the GetItemText() method to retrieve the string that represents the ListControl item text.

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        var dt = GetData();
        if (dt != null) {
            listBox1.DisplayMember = dt.Columns[0].ColumnName;
            listBox1.DataSource = dt;
        }
    }
    
    private DataTable GetData()
    {
        using (var conn = new OleDbConnection(ConfigurationManager.ConnectionStrings["database"].ConnectionString))
        using (var cmd = new OleDbCommand("SELECT empName FROM empTable", conn)) {
            conn.Open();
            using (var reader = cmd.ExecuteReader()) {
                if (!reader.HasRows) return null;
                var dt = new DataTable();
                dt.Load(reader);
                return dt;
            }
        }
    }
    
    private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
    {
        if (e.Index < 0) return;
        var lbx = sender as ListBox;
        bool isItemSelected = ((e.State & DrawItemState.Selected) == DrawItemState.Selected);
        using (var bgBrush = new SolidBrush(isItemSelected ? Color.FromArgb(64, 64, 64) : lbx.BackColor))
        using (var itemBrush = isItemSelected ? new SolidBrush(lbx.ForeColor) : new SolidBrush(Color.LightGray)) {
            string itemText = lbx.GetItemText(lbx.Items[e.Index]);
            e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
            e.Graphics.FillRectangle(bgBrush, e.Bounds);
            e.Graphics.DrawString(itemText, e.Font, itemBrush, e.Bounds);
        }
        e.DrawFocusRectangle();
    }