Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

OK, so this one is a bit odd.

I have a pretty straightforward DataTable:

DataTable dt = new DataTable();
dt.Columns.Add("Display", typeof(string));
dt.Columns.Add("Value", typeof(string));
dt.Rows.Add("Equals", "==");
dt.Rows.Add("Is Less Than", "<");
dt.Rows.Add("Is Less Than Or Equal To", "<=");
dt.Rows.Add("Is Greater Than", ">");
dt.Rows.Add("Is Greater Than Or Equal To", ">=");

Now, if I attach this DataTable to a brand new ComboBox, everything works as it should.

 comboBox1.DataSource = dt;
 comboBox1.ValueMember = "Value";
 comboBox1.DisplayMember = "Display";

This control displays Equals, Is Less Than, etc.

In my form, however, I have another ComboBox that is dynamically created, given a dynamic name based on which row in a TableLayoutPanel it is in, etc. When I use the exact same code for it, it displays System.Data.DataRowView for each dropdown selection:

Screenshot

So, rather than me going through each line of code one by one and trying to reverse engineer this, can someone tell me what would cause this to happen? Is there some predictable and repeatable way to cause this behaviour? I've tried all of the following to reverse it but nothing works:

dtecComboAction.DataSource = null;
dtecComboAction.DataBindings.Clear();
dtecComboAction.Items.Clear();
dtecComboAction.BindingContext = this.BindingContext;

I'm sure I'm doing something obvious somewhere else in my code but I can't figure it out.

Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
259 views
Welcome To Ask or Share your Answers For Others

1 Answer

The unexpected behavior described in the question is determined by a custom drawing of the OwnerDrawn ComboBox (a Custom Control derived from ComboBox).

When the DataSource of a ListControl is a complex object - as in this case, a DataTable.DefaultView (a DataView) - each Item in the list is a complex object itself, a DataRowView here.

For this reason, when drawing the Items of an OwnerDrawn ListControl (overriding the OnDrawItem method), the Item's text, as displayed in the Control's visible area, should be fetched using the ListControl's GetItemText() method.

This method fetches an Item's text (using the internal DataManager if a DataSource has been defined, or the TypeDescriptor if not), no matter what the Item's Type is:

If the DisplayMember property is not specified, the value returned by GetItemText(Object) is the value of the item's ToString method. Otherwise, the method returns the string value of the member specified in the DisplayMember property for the object specified in the item parameter.

.Net Source code of ListControl.GetItemText()

A classic mistake when drawing an Item's text it to convert to string the Item's object.
This can work if the Item's text is a simple string, not if it's a complex object. In this case [ComplexObject].ToString() returns the object's data type (System.Data.DataRowView, in this case).

protected override void OnDrawItem(DrawItemEventArgs e)
{
    // (...)
    e.DrawBackground();
    using (var brush = new SolidBrush(ForeColor)) {
        e.Graphics.DrawString(Items[e.Index].ToString()), Font, brush, e.Bounds);
    }
    // (...)
}

Simplified sample code, do not use :)

Using GetItemText(), the property value of DisplayMember is returned as a string. Since comboBox1.DisplayMember = "Display";, it will return the content of the DataTable's Display Column.

protected override void OnDrawItem(DrawItemEventArgs e)
{
    // (...)
    e.DrawBackground();
    using (var brush = new SolidBrush(ForeColor)) {
        e.Graphics.DrawString(GetItemText(Items[e.Index]), Font, brush, e.Bounds);
    }
    // (...)
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...