可能是为了保持平台独立性,SWT没有开放许多控件的自定义接口。例如,Win32中的Button、Label、List和ComboBox都是可以自绘(Owner Draw)的,但是SWT并没有把这些绘制方法开放出来。在最新的3.2版本中添加的一个新特性是Table和Tree现在支持Custom Draw了(但是并未整合到Viewer体系中),不过对于上述控件的支持仍付阙如。
上一次,我实现了一个自绘的按钮。现在,看到有人询问是否可以在Combo的列表中加入图像。其实这相当容易,只要重载Combo Widget并把自绘接口暴露出来即可。以下是简单的代码示例:
package org.eclipse.swt.widgets;
import java.io.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.internal.win32.*;
public class CustomCombo extends Combo
{
    public CustomCombo( Composite parent, int style )
    {
        super( parent, style );
        try
        {
            InputStream is = getClass().getResourceAsStream( "bullet.gif" );
            image = new Image( getDisplay(), is );
            is.close();
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        final int CB_SETITEMHEIGHT = 0x0153;
        OS.SendMessage( handle, CB_SETITEMHEIGHT, 0, 24 );
        OS.SendMessage( handle, CB_SETITEMHEIGHT, -1, 24 );
    }
    @Override
    int widgetStyle()
    {
        final int CBS_OWNERDRAWFIXED = 0x0010;
        final int CBS_HASSTRINGS = 0x0200;
        // final int CBS_OWNERDRAWVARIABLE = 0x0020;
        return super.widgetStyle() | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS;
    }
    @Override
    protected void checkSubclass()
    {
    }
    @Override
    public void dispose()
    {
        image.dispose();
        super.dispose();
    }
    /* @Override
    LRESULT wmMeasureChild( int wParam, int lParam )
    {
        MEASUREITEMSTRUCT mis = new MEASUREITEMSTRUCT();
        OS.MoveMemory( mis, lParam, MEASUREITEMSTRUCT.sizeof );
        mis.itemHeight = 40;
        OS.MoveMemory( lParam, mis, MEASUREITEMSTRUCT.sizeof );
        return null; // super.wmMeasureChild( wParam, lParam );
    } */
    @Override
    LRESULT wmDrawChild( int wParam, int lParam )
    {
        DRAWITEMSTRUCT dis = new DRAWITEMSTRUCT();
        OS.MoveMemory( dis, lParam, DRAWITEMSTRUCT.sizeof );
        GC gc = new GC( new DCWrapper( dis.hDC ) );
        Rectangle rc = new Rectangle( dis.left, dis.top, dis.right - dis.left,
                dis.bottom - dis.top );
        Display display = getDisplay();
        if ( (dis.itemState & OS.ODS_SELECTED) != 0 )
        {
            gc
                    .setBackground( display
                            .getSystemColor( SWT.COLOR_LIST_SELECTION ) );
            gc.setForeground( display
                    .getSystemColor( SWT.COLOR_LIST_SELECTION_TEXT ) );
            gc.fillRectangle( rc );
        }
        else
        {
            gc.setBackground( display
                    .getSystemColor( SWT.COLOR_LIST_BACKGROUND ) );
            gc.setForeground( display
                    .getSystemColor( SWT.COLOR_LIST_FOREGROUND ) );
            gc.fillRectangle( rc );
        }
        String text = getItem( dis.itemID );
        gc.drawImage( image, dis.left + 1, dis.top + 1 );
        gc.drawText( text, dis.left + 20, dis.top );
        gc.dispose();
        return null;
    }
    private static class DCWrapper implements Drawable
    {
        private int    hdc;
        DCWrapper( int hdc )
        {
            this.hdc = hdc;
        }
        public int internal_new_GC( GCData data )
        {
            return hdc;
        }
        public void internal_dispose_GC( int handle, GCData data )
        {
        }
    }
    private Image    image;
}
值得说明的是,如果设置Combo为OwnerDraw Variable风格,则必须重载wmMeasureChild方法来指定每一项的高度。如果使用OwnerDraw Fixed风格,则只需要在构造的时候发送一条CB_SETITEMHEIGHT消息就行了。
 另外一种值得考虑的选择是将Win32的ComboBoxEx控件包装成SWT Widget。不过,这需要转换若干结构并提供接口,Win32的ImageList管理机制和SWT的Image包装方法差别比较大,使得这种方法实现起来麻烦的多。