對于像combobox等控件,我們可以通過設置它們的drawmode特性(attribute)來對其進行繪制.
為了方便起見,我們先定義一個類stringcolorobject(這與ownerdraw本身沒有關系,只是針對這里要使用到的combobox而特意書寫的一個類.類的命名得益于quickstart).這個類本身很簡單:
using system.drawing;
namespace ownerdrawsample
{
public class stringcolorobject
{
//field to hold the string representing the color
private string colorrepresent;
//field to hold the actually color,the value of the string colorrepresent
private color color;
//the constructor
//takes 2 parameters, the color representation and the color itself
public stringcolorobject(string colorrepresent,color color)
{
this.colorrepresent = colorrepresent;
this.color = color;
}
//some attributes
public string colorrepresentation
{
get
{
return this.colorrepresent;
}
//only getter,no setter
}
public color color
{
get
{
return this.color;
}
//only getter,no setter
}
//override the tostring method in system.object
public override string tostring()
{
return this.colorrepresent;
}
//override the gethashcode method
public override int gethashcode()
{
//return the key field’s hash code
return this.color.gethashcode();
}
//override the equals method
public override bool equals(object obj)
{
if(!(obj is stringcolorobject))
return false;
return this.color.equals(((stringcolorobject)obj).color);
}
}
}
建立一個windows application,并從工具箱中拖拽一個combobox到form中去,并命名為cmbcolors然后在form的構造函數中添加:
//
// todo: 在initializecomponent調用后添加任何構造函數代碼
//
this.fillcomboxcolor(this.cmbcolors);
this.cmbcolors.selectedindex = 0;
其中,fillcomboxcolor(combobox )為自定義函數,它用來填充參數指定的combobox對象.它的實現為:
private void fillcomboxcolor(combobox cmb){
cmb.items.addrange(new object[]{
new stringcolorobject("黑色(black)",color.black),
new stringcolorobject("藍色(blue)",color.blue),
new stringcolorobject("深紅(dark red)",color.darkred),
new stringcolorobject("綠色(green)",color.green),
//……
});
}
上面只是一些準備工作,ownerdraw的實際工作現在才剛剛開始.將combobox對象cmbcolors的drawmode設置為ownerdrawfixed(或是ownerdrawvariable,這里設置為ownerdrawfixed模式).附帶說明一下:drawmode可以取normal,ownerdrawfixed和ownerdrawvariable三者之一.其中,normal模式表示控件由操作系統繪制,并且元素的大小都相等;ownerdrawfixed模式表示控件由手工繪制,并且元素的大小都相等;ownerdrawvariable則是表示控件由手工繪制,并且元素的大小可以不相等。
下面開始ownerdraw的核心部分:
1) 撰寫控件的measureitem事件
當要顯示combobox的某一項(item)的時候[即是單擊combobox的下拉按鈕時],這個事件會被首先喚起。它用來測量item的長度,寬度信息。例如:
//注冊事件
this.cmbcolor.measureitem += new measureitemeventhandler(this.cmbcolor_measureitem);
//實現cmbcolor_measureitem---測量combobox的每一個item
private void cmbcolor_measureitem(object sender, system.windows.forms.measureitemeventargs e)
{
//一般來說,這里需要設置的是measureitemeventargs對象e的itemheight屬性和itemwidth屬性
combobox cmb = (combobox)sender;
e.itemheight = cmb.itemheight;//當然,你可以寫e.itemheight = 20;
//不過對于ownerdrawfixed模式,這個函數好像都是多余.(對于combobox而言如此)
//why?????????????more work is essential.
}
2) 撰寫控件的drawitem事件
combobox中的項的繪制任務將在drawitem事件中完成.類似地,先注冊該事件,然后再添加適當的代碼完成對事件的響應:
//注冊事件
this.cmbcolor.drawitem += new
drawitemeventhandler(this.cmbcolor_drawitem);
//繪制item
private void cmbcolor_drawitem(object sender, system.windows.forms.drawitemeventargs e)
{
combobox cmb = (combobox)sender;
if(cmb == null) return;
int index = e.index; //獲取要繪制的item的index(索引)
if(index == -1) return;
//如果item被選擇,繪制正確的背景色
e.drawbackground();
e.drawfocusrectangle();//因為item被選擇,繪制焦點矩形
graphics g = e.graphics;
//根據item的index獲取對應的顏色
color c = ((stringcolorobject)cmbcolor.items[index]).color;
rectangle rectcolor = e.bounds;//獲取包圍item的邊框,并存儲在rectcolor中
//將rectcolor做適當變換,這不會影響到e.bounds本身
rectcolor.offset(2,2);//右移2,下移2
rectcolor.width = 20;//寬度設置為20
rectcolor.height -= 4;//高度減4
g.drawrectangle(new pen(c),rectcolor);//繪制rectcolor代表的矩形,即是在圖中看到的item左邊的矩形部分
//再次將rectcolor做變換,并填充之
rectcolor.offset(2,2);
rectcolor.width = 17;//要注意,在c#中繪制和填充
rectcolor.height -= 3;// 對最后一個象素點的不同處理方式
g.fillrectangle(new solidbrush(c),rectcolor);//填充rectcolor矩形,這個矩形就是在圖中看到的item左邊的那個被填充的矩形
g.drawstring(((stringcolorobject)cmbcolor.items[index]).tostring(),
this.font,new solidbrush(c),e.bounds.left + 25,e.bounds.top);
//繪制字符串
}
這段程序會運行得很好.為了能夠讀懂程序,你得知道如下的一些概念:
1). e.index measureitemeventargs對象的index屬性表達的意思是欲繪制的item的索引值之義.對于上面的例子,黑色(black),藍色(blue)…分別對應著index的0,1…(這種說法不是很嚴密,但易于接受.比較嚴密的說法是,stringcolorobject對象對應著index…).
2). e.graphics 這是要在item上繪制的graphics對象.有了它,就能在item上實現圖形的繪制了.
3). e.bounds 這是要繪制的item的邊界矩形.
知道了這些概念,就不難理解上面drawitem完成的操作以及為什么會那樣去完成。
對于menuitem的手工繪制,你要做的事和上面差不多.不同的是,你需要將其ownerdraw由默認的false設置為true.然后再measureitem,drawitem.如果你親身實踐,你會發現如果你不注冊measureitem并輔以相應代碼,你不會看到那個menuitem.
---the end