关于 ListView 排序小箭头的若干历史问题的决议(100)

  • 主题发起人 主题发起人 vvyang
  • 开始时间 开始时间
V

vvyang

Unregistered / Unconfirmed
GUEST, unregistred user!
一、如何在 ListView 的标题栏上画排序小箭头?看似一个老调重弹的问题,您在网上问这个问题,会有多种答案:方法1:用 ImageList如果用那个 ImageList 的话,图片会出现在文字的左边,而且在 XP 环境下,由于图片不透明,鼠标一指 Hot 的话,会露出图片底色。方法2:用 HDF_SORTUP、HDF_SORTDOWN 等 Windows 提供的 HeaderControl 格式为 HeaderControl 的 HDItem.fmt 添加 HDF_SORTDOWN 或 HDF_SORTUP 属性,可以画出与系统一样的排序箭头,但是很可惜,HDF_SORTDOWN、HDF_SORTUP 只能运行在 XPTheme 下,如果在 2000/98 下或程序没有 XP 风格,都无法显示。方法3:用某“改良版” ListView网上流传一种所谓“改良版”的 ListView,主要原理是靠覆盖 ListView 的 WndProc 方法,并截获 HDN_ITEMCLICK 消息,向 HeaderControl 的 HdItem.hbm 赋予 Bitmap 以实现排序箭头效果。很显然,作者在这样写完后发现了一个问题,就是一旦表头的宽度变化或标题变化或左右方向变化,都会导致箭头消失,于是作为补救,他又截获了 HDN_ENDTRACK、HDN_ENDTRACKW、HDN_ITEMCHANGED 消息,以期箭头消失后再画上一个。问题又来了,许多人问为什么这种方法在 2000/98 下运行正常,而一在 XP 下就 StackOverFlow?很简单,在 XP 下这样写会导致 HDN_ITEMCHANGED 消息不断被触发,从而进入死循环!二、因为最近要用一个带排序箭头的 ListView,而苦于手头没一个能用的,于是只好自己写。看了 Delphi ListView 的源码,真怀疑 Borland 的程序员在写这段代码的时候是不是在睡觉!尤其是 TListView.UpdateColumn 方法(此方法是画标题栏的关键,在多处被调用),不管 LVColumn 的 fmt、cx、pszText 等属性改没改变,只要有一个变了,其它也一律用 ListView_SetColumn 重新写回;尤其令人不能容忍的是,他竟然想当然地认为 fmt 只有 LVCFMT_IMAGE、LVCFMT_LEFT、LVCFMT_RIGHT、LVCFMT_CENTER 这几个属性!!稍微有点常识的人,翻开 MSDN,都知道 fmt 还有 LVCFMT_BITMAP_ON_RIGHT 可以控制图片在文字左边还是右边,还有 LVCFMT_COL_HAS_IMAGES 可以控制标题栏是否要显示图片,不仅如此,fmt 还有一些没有在文档中描述的属性(如 $4000),而且 LVColumn.fmt 与 HDItem.fmt 关系密切,也就是说 HeaderControl 的 HdItem 会影响 ListView 的 LVColumn。以上也是为什么第一种方法图片会出现在文字的左边(没有 LVCFMT_BITMAP_ON_RIGHT)、第三种方法表头变化箭头就会消失(触发 TListView.UpdateColumn 会导致所有工作白做)的原因。三、解决问题的关键就在修改 TListView.UpdateColumn,是它的设计缺陷导致所有画箭头的努力最终归于失败。很可惜,Delphi 的 TListView.UpdateColumn 竟然是静态方法,不能覆盖,也就是说 TListView 的设计人员不想让别人去发挥想象力,只能用那个狗屎般的 ImageList。我只好把 ComCtrls.pas 单元中与 ListView 有关的类都单独摘出来,然后在所有类前加个 Sort 以避免与原有类同名,接下来的工作就简单了:首先大改 TListView.UpdateColumn 方法,尤其是要把“LVColumn.fmt := LVCFMT_LEFT”之类的换成“LVColumn.fmt := LVColumn.fmt or LVCFMT_LEFT”,以保留原来 fmt 的基因;至于画箭头,就不要用脱裤子放屁的 LoadImage、LineTo 之类的玩意,慢啊!创建个16×16内存位图,背景透明后直接用 Marlett 字体往上写“5”(向上箭头)、写“6”(向下箭头)就行了(事实上 Windows 所有窗口的关闭、最大化、最小化、恢复、帮助、上下左右箭头都是用 Marlett 字体画上去的,系统御用);还有一个问题,位图画箭头在 2000/98 下没有问题,但在 XPTheme 下鼠标一指 Hot 还是会露出图片底色,这时改用前面介绍的第二种方法就行了;至于如何区分程序是否具有 XP 风格,用 GetFileVersion 判断当前程序调用的是哪一个版本的 comctl32.dll 就行了;另外为这个类加了个 SortColumn 属性,可以设置哪个列需要排序箭头。四、不知以前是否有人用过此类方法,如果有,就当我白写。代码已上传到 Delphi 盒子 http://www.2ccc.com感兴趣的话可以看一看,如有缺陷或有更好的方法请指出,谢谢!
 
友情路过
 
先赞一个!
 
下载不下来,手头有段代码,procedure ListView_ColumnClick(Sender: TObject; Column: TListColumn); function CustomSortProc(Item1, Item2: TListItem; ParamSort: longint): Integer; stdcall; var byteHi, byteLow: Byte; begin byteHi := Byte((ParamSort and $0000FF00) shr 8); byteLow := Byte(ParamSort and $000000FF); if byteHi = 0 then begin if byteLow = 0 then Result := -comparetext(Item1.caption, Item2.caption) else Result := comparetext(Item1.caption, Item2.caption) end else begin if byteLow = 0 then begin // if (Sender as TListView).Name = 'lvAlarm' then begin if (byteHi = 1) then //日期 Result := -comparetext(Item1.SubItems.Strings[byteHi - 1] + Item1.SubItems.Strings[byteHi], Item2.SubItems.Strings[byteHi - 1] + Item2.SubItems.Strings[byteHi]) else if (byteHi = 2) then //时间 Result := -comparetext(Item1.SubItems.Strings[byteHi - 2] + Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 2] + Item2.SubItems.Strings[byteHi - 1]) else Result := -comparetext(Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 1]) end; // else // Result := -comparetext(Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 1]) end else begin // if (Sender as TListView).Name = 'lvAlarm' then begin if (byteHi = 1) then //日期 Result := comparetext(Item1.SubItems.Strings[byteHi - 1] + Item1.SubItems.Strings[byteHi], Item2.SubItems.Strings[byteHi - 1] + Item2.SubItems.Strings[byteHi]) else if (byteHi = 2) then //时间 Result := comparetext(Item1.SubItems.Strings[byteHi - 2] + Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 2] + Item2.SubItems.Strings[byteHi - 1]) else Result := comparetext(Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 1]) end; // else // Result := comparetext(Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 1]) end; // Result := comparetext(Item1.SubItems.Strings[byteHi - 1], Item2.SubItems.Strings[byteHi - 1]); end; end;var i, iPos: Integer; s: string; tmpLV: TListView;begin tmpLV := Sender as TListView; if pos('▲', Column.caption) > 0 then begin s := Column.caption; iPos := pos('▲', s); if Column.index = 0 then //第1个是Caption tmpLV.CustomSort(@CustomSortProc, $00000000) else tmpLV.CustomSort(@CustomSortProc, $00000000 or (word(Column.index) shl 8)); Delete(s, iPos, 2); Column.caption := s + '▼'; end else if pos('▼', Column.caption) > 0 then begin s := Column.caption; iPos := pos('▼', s); if Column.index = 0 then //第1个是Caption tmpLV.CustomSort(@CustomSortProc, $000000FF) else tmpLV.CustomSort(@CustomSortProc, $000000FF or (word(Column.index) shl 8)); Delete(s, iPos, 2); Column.caption := s + '▲'; end else begin if Column.index = 0 then //第1个是Caption tmpLV.CustomSort(@CustomSortProc, $00000000) else tmpLV.CustomSort(@CustomSortProc, $00000000 or (word(Column.index) shl 8)); Column.caption := trim(Column.caption) + ' ▼'; end; for i := 0 to tmpLV.Columns.Count - 1 do begin if Column = tmpLV.Columns then continue; s := tmpLV.Columns.caption; iPos := pos('▲', s); if iPos > 0 then begin Delete(s, iPos, 2); tmpLV.Columns.caption := s; end; iPos := pos('▼', s); if iPos > 0 then begin Delete(s, iPos, 2); tmpLV.Columns.caption := s; end; end;end;//////////////////////procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn);begin ListView_ColumnClick(Sender, Column);end;//////////请测试
 
To zhengrong117:用“标题+▲”的方式我不太喜欢,就不测试了,呵呵...ps:广西镜像能下...
 
收摊啦...
 
后退
顶部