views:

83

answers:

2

Hello,
I am creating a custom combobox which can draw separators. So, I override OnDrawItem() and OnMeasureItem() methods.
The problem is that OnMeasureItem() is called only once when datasource is changed. So if I want to specify a separator item later I need to remeasure it's height (because items with separator should be taller), but it seems that all methods that can lead to item height being remeasured are private, so I can't call them.
I don't know if it's easy to understand what I've written above, so I will repeat what I need:
I need to remeasure item height (OnMeasureItem() must be called) each time when I specify that the item should be drawn with a separator.

separatorableComboBox.DataSource = customers;
// and now I want the third customer in the list to be drawn with a separator, 
// so it needs to be taller and therefore OnMeasureItem() should be called
separatorableComboBox.SpecifySeparatorItem(customers[2]);

UPD. Guys, calling RefreshItems() works, but it's very slow (> 20 ms on my machine), are there faster methods?
UPD2. Right now I am using SendMessage(..., CB_SETITEMHEIGHT, ...); method as serge_gubenko advised. But I'm just curious if there is a fast way of accomplishing the task with .NET (or more specifically with ComboBox class itself)?

+4  A: 

You can call ComboBox.RefreshItems() in order to raise MeasureItem() calls, either inside your SpecifySeparatorItem() of your CustomCombo class :

public void SpecifySeparatorItem(object arg)
{
    //do some stuff

    this.RefreshItems();

    //do some more stuff
}

Or by exposing ComboBox.RefreshItems() through a public method that you can call elsewhere

public partial class CustomCombo : ComboBox
{
    public CustomCombo ()
    {
        InitializeComponent();
    }

    protected override void OnMeasureItem(MeasureItemEventArgs e)
    {
        base.OnMeasureItem(e);
    }

    public void RaiseOnMeasureItem()
    {
        this.RefreshItems();
    }
}
Dynami Le Savard
Guys, calling RefreshItems() works, but it's very slow (> 20 ms on my machine), are there faster methods?
nightcoder
+2  A: 

Just to clarify, I assume you're using OwnerFrawVariable style for your combobox. If I understood your question correctly, there are couple of ways to do what you need:

1.you can call RefreshItems method of the combobox which would recreate items and trigger onMeasureItem event for each item. This method is protected for the ComboboxClass, so below is an example on how you could do it using reflection:

MethodInfo method = comboBox1.GetType().GetMethod(
    "RefreshItems", BindingFlags.Instance | BindingFlags.NonPublic);
if (method != null) method.Invoke(comboBox1, null);

2.another way is to send CB_SETITEMHEIGHT message to the control with new height of the item whenever you want to change it. Below is an example:

public const int CB_SETITEMHEIGHT = 0x0153;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);
...
// this will set height to 100 for the item with index 2 
SendMessage(comboBox1.Handle, CB_SETITEMHEIGHT, 2, 100); 

hope this helps, regards

serge_gubenko
Sending a message works fast, but I'm just curious if there is a more correct and also fast way of accomplishing the task with managed code (more specifically maybe with ComboBox class)?
nightcoder
I wouldn't worry about it too much when using winforms, eventually there is still a windows COMBOBOX control (http://msdn.microsoft.com/en-us/library/bb775792(VS.85).aspx) running on your form; you can check it with spy++. ComboBox class doing the same when adjusting item's height in its UpdateItemHeight() method, which is private so you need to use reflection to call it. As for RefreshItems it's completely reloading the list of items. Basically it should be the same to setting null to its DataSource property and then restoring it back to previous value
serge_gubenko