现在的位置: 首页 > 综合 > 正文

在SWT中实现自绘Combo

2012年12月19日 ⁄ 综合 ⁄ 共 3566字 ⁄ 字号 评论关闭
可能是为了保持平台独立性,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, 024 );
        OS.SendMessage( handle, CB_SETITEMHEIGHT, 
-124 );
    }

    @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包装方法差别比较大,使得这种方法实现起来麻烦的多。

抱歉!评论已关闭.