自画TlistView带进度条的Item - [学习资料]
<div class=postBody>
版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://proc.blogbus.com/logs/28162911.html
<div style="TEXT-INDENT: 21.75pt">TListView的Item条一般是由系统自画的,但电驴就实现了自画,使之看起来很漂亮,我们用DELPHI也可以实现!</div><div style="TEXT-INDENT: 21.75pt">
</div><div style="TEXT-INDENT: 21.75pt">首先要引用
CommCtrl单元,这是TListView底层控制单元:</div><div align=left>
uses</div><div align=left>
CommCtrl;</div><div align=left>
//画状态条</div><div align=left>
procedure DrawSubItem(LV: TListView; Item: TListItem; SubItem: Integer;</div><div align=left>
Prosition: Single; Max, Style: Integer; IsShowProgress: Boolean;</div><div align=left>
DrawColor: TColor = $00005B00;</div><div align=left>
FrameColor: TColor = $00002F00);</div><div align=left>
//获取SubItem的区域</div><div align=left>
function GetItemRect(LV_Handle, iItem, iSubItem: Integer): TRect;</div><div align=left>
var</div><div align=left>
Rect: TRect;</div><div align=left>
begin</div><div align=left>
ListView_GetSubItemRect(LV_Handle, iItem, iSubItem, LVIR_LABEL, @Rect);</div><div align=left>
Result := Rect;</div><div align=left>
end;</div><div align=left>
var</div><div align=left>
PaintRect, r: TRect;</div><div align=left>
i, iWidth, x, y: integer;</div><div align=left>
S: string;</div><div align=left>
begin</div><div align=left>
try</div><div align=left></div><div align=left>
with lv do</div><div align=left>
begin</div><div align=left>
//LockPaint := True;</div><div align=left>
PaintRect := GetItemRect(LV.Handle, Item.Index, SubItem);</div><div align=left>
r := PaintRect;</div><div align=left>
// if SubItem = DrawSubItem then</div><div align=left>
Begin</div><div align=left>
//这一段是算出百分比</div><div align=left>
if Prosition >= Max then</div><div align=left>
Prosition := 100</div><div align=left>
else</div><div align=left>
if Prosition <= 0 then</div><div align=left>
Prosition := 0</div><div align=left>
else</div><div align=left>
Prosition := Round((Prosition / Max) * 100);</div><div align=left>
if (Prosition = 0) and (not IsShowProgress) then</div><div align=left>
begin </div><div align=left>
//如果是百分比是0,就直接显示空白</div><div align=left>
Canvas.FillRect(r);</div><div align=left>
end</div><div align=left>
else</div><div align=left>
begin</div><div align=left>
//先直充背景色</div><div align=left>
Canvas.FillRect(r);</div><div align=left>
Canvas.Brush.Color := Color;</div><div align=left>
// Canvas.FillRect(r);</div><div align=left>
//画一个外框</div><div align=left>
InflateRect(r, -2, -2);</div><div align=left>
Canvas.Brush.Color := FrameColor; //$00002F00;</div><div align=left>
Canvas.FrameRect(R);</div><div align=left>
Canvas.Brush.Color := Color;</div><div align=left>
InflateRect(r, -1, -1);</div><div align=left>
// Canvas.FillRect(r);</div><div align=left>
InflateRect(r, -1, -1);</div><div align=left>
//根据百分比算出要画的进度条内容宽度</div><div align=left>
iWidth := R.Right - Round((R.Right - r.Left) * ((100 - Prosition) /</div><div align=left>
100));</div><div align=left>
case Style of</div><div align=left>
0: //进度条类型,实心填充</div><div align=left>
begin</div><div align=left>
Canvas.Brush.Color := DrawColor;</div><div align=left>
r.Right := iWidth;</div><div align=left>
Canvas.FillRect(r);</div><div align=left>
end;</div><div align=left>
1: //进度条类型,竖线填充</div><div align=left>
begin</div><div align=left>
i := r.Left;</div><div align=left>
while i < iWidth do</div><div align=left>
begin</div><div align=left>
Canvas.Pen.Color := Color;</div><div align=left>
Canvas.MoveTo(i, r.Top);</div><div align=left>
Canvas.Pen.Color := DrawColor;</div><div align=left>
canvas.LineTo(i, r.Bottom);</div><div align=left>
Inc(i, 3);</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
end;</div><div style="TEXT-INDENT: 45pt" align=left>
//画好了进度条后,现在要做的就是显示进度数字了</div><div align=left>
Canvas.Brush.Style := bsClear;</div><div align=left>
if Prosition = Round(Prosition) then</div><div align=left>
S := Format('%d%%', [Round(Prosition)])</div><div align=left>
else</div><div align=left>
S := FormatFloat('#0.0', Prosition);</div><div align=left>
with PaintRect do</div><div align=left>
begin</div><div align=left>
x := Left + (Right - Left + 1 - Canvas.TextWidth(S)) div 2;</div><div align=left>
y := Top + (Bottom - Top + 1 - Canvas.TextHeight(S)) div 2;</div><div align=left>
end;</div><div align=left>
SetBkMode(Canvas.handle, TRANSPARENT);</div><div align=left>
Canvas.TextRect(PaintRect, x, y, S);</div><div align=left>
end;</div><div style="TEXT-INDENT: 36pt" align=left>
//进度条全部画完,把颜色设置成默认色了</div><div align=left>
Canvas.Brush.Color := Color;</div><div align=left>
end</div><div align=left>
end;</div><div align=left>
except</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
上面是画进度条的,现在要给TlistView处理Item重绘的消息,事件是OnCustomDrawItem,需要说明的是,如果想要随心所欲的自画Item,那么就要全部自己来完成,不再需要系统来处理:</div><div align=left>
procedure TForm1.ListView1CustomDrawItem(</div><div align=left>
Sender: TCustomListView; Item: TListItem; State: TCustomDrawState;</div><div align=left>
var DefaultDraw: Boolean);</div><div align=left>
var</div><div align=left>
BoundRect, Rect: TRect;</div><div align=left>
i: integer;</div><div align=left>
TextFormat: Word;</div><div align=left>
LV: TListView;</div><div align=left>
//这个子过程是用来画CheckBox和ImageList的</div><div align=left>
procedure Draw_CheckBox_ImageList(r: TRect; aCanvas: TCanvas; Checked: Boolean);</div><div align=left>
var</div><div align=left>
R1: TRect;</div><div align=left>
i: integer;</div><div align=left>
begin</div><div align=left>
if Sender.Checkboxes then</div><div align=left>
begin</div><div align=left>
aCanvas.Pen.Color := clBlack;</div><div align=left>
aCanvas.Pen.Width := 2;</div><div align=left>
//画CheckBox外框</div><div align=left>
aCanvas.Rectangle(r.Left + 2, r.Top + 2, r.Left + 14, r.Bottom - 2);</div><div align=left>
if Checked then</div><div align=left>
begin //画CheckBox的勾</div><div align=left>
aCanvas.MoveTo(r.Left + 4, r.Top + 6);</div><div align=left>
aCanvas.LineTo(r.Left + 6, r.Top + 11);</div><div align=left>
aCanvas.LineTo(r.Left + 11, r.Top + 5);</div><div align=left>
end;</div><div align=left>
aCanvas.Pen.Width := 1;</div><div align=left>
end;</div><div align=left>
//开始画图标</div><div align=left>
i := PDownLoadListItem(Item.Data)^.StatsImageIndex;</div><div align=left>
if i > -1 then</div><div align=left>
begin</div><div align=left>
//获取图标的RECT</div><div align=left>
if Boolean(ListView_GetSubItemRect(sender.Handle, item.Index, 0, LVIR_ICON, @R1)) then</div><div align=left>
begin</div><div align=left>
ImageList_Stats.Draw(LV.Canvas, R1.Left, R1.Top, i);</div><div align=left>
if item.ImageIndex > -1 then</div><div align=left>
LV.SmallImages.Draw(LV.Canvas, R1.Right + 2, R1.Top, item.ImageIndex);</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
begin</div><div align=left>
LV := ListView1;</div><div align=left>
BoundRect := Item.DisplayRect(drBounds);</div><div align=left>
InflateRect(BoundRect, -1, 0);</div><div align=left>
//这个地方你可以根据自己的要求设置成想要的颜色,实现突出显示</div><div align=left>
LV.Canvas.Font.Color := clBtnText;</div><div align=left>
//查看是否是被选中</div><div align=left>
if Item.Selected then</div><div align=left>
begin</div><div align=left>
if cdsFocused in State then</div><div align=left>
begin</div><div align=left>
LV.Canvas.Brush.Color := $00ECCCB9; // //clHighlight;</div><div align=left>
end</div><div align=left>
else</div><div align=left>
begin</div><div align=left>
LV.Canvas.Brush.Color := $00F8ECE5; //clSilver;</div><div align=left>
end;</div><div align=left>
end</div><div align=left>
else</div><div align=left>
begin</div><div align=left>
if (Item.Index mod 2) = 0 then</div><div align=left>
LV.Canvas.Brush.Color := clWhite</div><div align=left>
else</div><div align=left>
LV.Canvas.Brush.Color := $00F2F2F2;</div><div align=left>
end;</div><div align=left>
LV.Canvas.FillRect(BoundRect); //初始化背景</div><div align=left>
for i := 0 to LV.Columns.Count - 1 do</div><div align=left>
begin</div><div align=left>
//获取SubItem的Rect</div><div align=left>
ListView_GetSubItemRect(LV.Handle, Item.Index, i, LVIR_LABEL, @Rect);</div><div align=left>
case LV.Columns.Alignment of</div><div align=left>
taLeftJustify:</div><div align=left>
TextFormat := 0;</div><div align=left>
taRightJustify:</div><div align=left>
TextFormat := DT_RIGHT;</div><div align=left>
taCenter:</div><div align=left>
TextFormat := DT_CENTER;</div><div align=left>
end;</div><div align=left>
case i of</div><div align=left>
0: //画Caption,0就是表示Caption,这不是Subitems[0]</div><div align=left>
begin</div><div style="TEXT-INDENT: 45pt" align=left>
//先画选择框与图标</div><div align=left>
Draw_CheckBox_ImageList(BoundRect, LV.Canvas, Item.Checked);</div><div style="TEXT-INDENT: 45pt" align=left>
//再画Caption的文字</div><div align=left>
InflateRect(Rect, -(5 + ImageList_Stats.Width), 0); //向后移3个像素,避免被后面画线框时覆盖</div><div align=left>
DrawText(</div><div align=left>
LV.Canvas.Handle,</div><div align=left>
PCHAR(Item.Caption),</div><div align=left>
Length(Item.Caption),</div><div align=left>
Rect,</div><div align=left>
DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS or TextFormat);</div><div align=left>
end;</div><div align=left>
1..MaxInt: //画Subitems</div><div align=left>
begin</div><div align=left>
if i - 1 = 2 then //显示状态条</div><div align=left>
begin</div><div style="TEXT-INDENT: 45pt" align=left>
//开始处理进度条了,这个示例是第3栏显示进度条,可以自己随便定义</div><div align=left>
DrawSubItem(TListView(Sender),</div><div align=left>
item,</div><div align=left>
i,</div><div align=left>
StrToFloatDef(Item.SubItems[i - 1], 0),</div><div align=left>
100,</div><div align=left>
0,</div><div align=left>
True,</div><div style="TEXT-INDENT: 45pt" align=left>
//这里用了一个Lable来选颜色,你自己可以使用一个变量来代替</div><div align=left>
LableProgressColor.Color, //进度条外框颜色</div><div align=left>
LableProgressColor.Color //进度条颜色</div><div style="TEXT-INDENT: 63pt" align=left>
);</div><div align=left>
end</div><div align=left>
else</div><div style="TEXT-INDENT: 45pt" align=left>
//画SubItem的文字</div><div align=left>
if i - 1 <= Item.SubItems.Count - 1 then</div><div align=left>
DrawText(</div><div align=left>
LV.Canvas.Handle,</div><div align=left>
PCHAR(Item.SubItems[i - 1]),</div><div align=left>
Length(Item.SubItems[i - 1]),</div><div align=left>
Rect,</div><div align=left>
DT_VCENTER or DT_SINGLELINE or DT_END_ELLIPSIS or TextFormat);</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
end;</div><div align=left>
LV.Canvas.Brush.Color := clWhite;</div><div align=left>
if Item.Selected then //画选中条外框</div><div align=left>
begin</div><div align=left>
if cdsFocused in State then//控件是否处于激活状态</div><div align=left>
LV.Canvas.Brush.Color := $00DAA07A // $00E2B598; //clHighlight;</div><div align=left>
else</div><div align=left>
LV.Canvas.Brush.Color := $00E2B598; //$00DAA07A // clHighlight;</div><div align=left>
LV.Canvas.FrameRect(BoundRect); // </div><div align=left>
end;</div><div align=left>
DefaultDraw := False; //不让系统画了</div><div align=left>
with Sender.Canvas do</div><div align=left>
if Assigned(Font.OnChange) then Font.OnChange(Font);</div><div align=left>
end;</div><div align=left></div><div align=left>
function ReDrawItem(HwndLV: HWND; ItemIndex: integer): boolean;
begin
Result := ListView_RedrawItems(HwndLV, ItemIndex, ItemIndex);
end;</div><div align=left></div><div align=left>
//使用:</div><div align=left>
item:=ListView1.Selected;</div><div align=left>
item.subitems[1]:='30';//设置为30%</div><div align=left>
//然后刷新这个item
ReDrawItem(ListView1.handle,Item.index);
<div style="TEXT-INDENT: 21.75pt">不用进度条时的效果图:</div><div style="TEXT-INDENT: 21.75pt" align=center>
</div></div></div>