65.9K
CodeProject 正在变化。 阅读更多。
Home

C# List View v1.3

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (150投票s)

2003年4月26日

CPOL

13分钟阅读

viewsIcon

2301166

downloadIcon

56119

一个功能齐全、完全托管的C# ListView。

引言

欢迎来到我的Glacial ListView控件的第四次迭代v1.3。我最初在2002年12月开始这个项目,当时我正在为一个外包项目编写一个ListView,该项目需要显示基于团队的游戏(足球、篮球等)的分数。我从VS.NET自带的股票列表控件开始。在经历了太多次窗口过程、黑客和死胡同之后,我决定为这个以及未来的许多项目开发一个高度可定制的ListView。所以我开始了这项完全用C#编写的ListView项目。

在第一个版本v1.0中,我试图将一切都基于我在MFC中编写的一个类似控件的模型(结果并不理想)。我的第二个版本v1.1专注于优化和.NET中心化的代码库更改,并进行了一些重要的功能升级。第三个版本专注于功能,特别是控件嵌入。这次第四次更新主要集中在完善一切、修复bug以及更多的嵌入工作。

许多人发现我经常对这个控件进行bug修复和更新。为了避免因大量更新而打扰The Code Project的好人们,我会在我的网站上发布小的更新。您可以在Glacial Listview Control这里直接获取这些子更新。您也可以在那里找到更详细的更改日志。

此时,这个控件的功能已经太多,无法在此一一列出。保持源代码的整洁以便不断添加功能一直是一个真正的挑战。我添加的东西越多,我的文档和编码实践就必须越好,否则事情会很快变得难以管理。我正在努力创建文档,以用户指南的形式与控件一起提供,使使用一切变得更加容易。

特点

v1.3

项集合编辑器、激活嵌入、复选框(非嵌入式控件复选框)、帮助文件、悬停事件。

行边框、控件嵌入、新控件模式(XP、超平、普通)、XP样式、改进的排序、交替行颜色、改进的图像/图标支持、子项单元格自动换行、优化、列和项的热跟踪、升/降序排序、焦点矩形、半透明选择、项和子项级别的用户对象变量、多选、可调整列宽、网格选项、多行项、多行截断、排序、自动调整项高度、文本+对齐+颜色、背景颜色覆盖、基本ListView功能。

亮点

背景/前景颜色完全控制:您现在可以在几乎所有级别上控制控件的背景或前景颜色。我使用一个分层系统来控制显示哪种颜色。我采用最低公共覆盖率来确定给定单元格的背景颜色。例如,您可以为控件、项和子项设置背景颜色。给定单元格将显示子项的覆盖颜色。如果您只覆盖了行背景,那么该颜色将显示在整行上。前景颜色(文本颜色)也是如此。

项集合编辑器:您要求的功能,我实现了。现在您可以在集合编辑器中添加项,并直观地编辑子项。我已尽力使集合编辑器尽可能易于使用。

排序:提供3种排序方法。插入排序,非常适合长度小于1000的排序。快速排序和合并排序适用于长度大得多的列表。我还包含了按数字排序的功能,您可以在列属性中设置。

复选框:为您的项/子项添加复选框就像在列中设置属性一样简单。复选框绘制在控件上,因此您不必担心处理嵌入的二次控件。

图像:您可以根据需要为列标题或项/子项添加图像或图标。

悬停事件:如果您需要为特定列创建工具提示,您可以订阅悬停事件。请记住,您必须打开悬停事件才能触发悬停事件。

热跟踪:如果您为垂直和/或水平启用热跟踪,您将看到鼠标悬停的列/行以及您选择的颜色的高亮显示。

UserObjects/Tag:为了能够将您的用户数据存储到项中,我在项和子项级别都包含了Tag属性。注意:项Tag与SubItem[0] Tag不同。所以不要混淆这两者。它们是不同的。

交替行颜色:这项功能最终非常受欢迎,所以我最近实现了它。要获得那种“支票簿”的外观和感觉,只需打开交替行颜色并设置您想要的颜色。

控件嵌入:您可以轻松嵌入原始控件或激活的控件。通过添加进度条、DateTime控件或您自己的自定义控件,让您的listview拥有专业的外观。

使用控件

该接口模仿了.NET框架内置的股票ListView,因此操作ListView的许多方法在此实现中都适用。有人希望有更多关于如何使用各种功能的示例,所以我添加了更多信息。

此控件的大部分主要功能都可以在设计编辑器中找到,当您将控件放置到页面上时。从热跟踪、网格线到选择颜色等所有内容都可以通过MS设计时环境进行可视化操作。

使用此控件

  1. 在项目的引用部分包含对GlacialList.DLL的引用。
  2. 在类文件头部添加using GlacialComponents.Controls指令。

将此控件添加到您的工具箱

  1. 转到工具/自定义工具箱/.NET Framework组件
  2. 点击浏览并导航到GlacialList.DLL并添加它。
GlacialList mylist = new GlacialList();

mylist.Columns.Add( "Column1", 100 ); // this can also be added 
         // through the design time support 
mylist.Columns.Add( "Column2", 100 ); 
mylist.Columns.Add( "Column3", 100 ); 
mylist.Columns.Add( "Column4", 100 ); 

GLItem item;

item = this.glacialList1.Items.Add( "Atlanta Braves" );
item.SubItems[1].Text = "8v";
item.SubItems[2].Text = "Live";
item.SubItems[2].BackColor = Color.Bisque;
item.SubItems[3].Text = "MLB.TV"; 

item = this.glacialList1.Items.Add( "Florida Marlins" );
item.SubItems[1].Text = "";
item.SubItems[2].Text = "Delayed";
item.SubItems[2].BackColor = Color.LightCoral;
item.SubItems[3].Text = "Audio";


item.SubItems[1].BackColor = Color.Aqua; // set the background 
      // of this particular subitem ONLY
item.UserObject = myownuserobjecttype; // set a private user object
item.Selected = true; // set this item to selected state
item.SubItems[1].Span = 2; // set this sub item to span 2 spaces

ArrayList selectedItems = mylist.SelectedItems; 
           // get list of selected items

嵌入

GlacialList中有两种嵌入类型。第一种是标准控件嵌入,基本上是单元格与嵌入控件的一对一关系。在ListView中不可见的控件被“隐藏”但未被销毁。第二种是激活嵌入,这种嵌入只有在您双击单元格时才会显示,并且对于列中的每个单元格都是相同的。

我在添加嵌入式控件时面临的一个重大挑战是其他列表控件都未解决的问题。如何使嵌入式控件显示在边框后面。由于边框几乎总是绘制在控件上,因此无法简单地在嵌入式控件之上绘制。我通过添加我创建的5个BorderStrip控件来解决这个问题,以产生边框在嵌入式控件之上的效果。其他控件要么没有此功能,要么完全删除边框来解决这个问题。

标准控件嵌入

上图显示了一个嵌入的进度条控件。

关于嵌入的一点提示。我为您提供了您想要的ListView的工具。但是,如果您在表面上加载10万个控件,那么如果您以每小时1帧的速度滚动,您只能怪自己。您必须明智地决定哪种权衡最适合您想要实现的目标。

当我第一次解决控件嵌入问题时,我知道这不会容易。要获得坚实、专业的成果,我必须在功能和性能之间进行许多权衡。在我开始编写控件代码之前,我先编写了几个测试项目来检验我关于大量可见控件在活动表面上性能的各种理论。这些测试使我能够在不损害性能的情况下优化功能集。

我必须处理的一个权衡是,当控件移出视图时,是销毁它还是隐藏它。如果您每次都销毁和重新创建控件,可以节省内存和句柄,但会牺牲速度。如果您隐藏移出视图的控件,那么当您有大量项时,ListView会开始拖慢系统。我决定,如果有人使用嵌入式控件,他们不太可能有10万+个项,所以我会“隐藏”当前视图之外的控件。

要使用控件嵌入,您只需将控件添加到SubItem.Control属性即可。您也可以通过设置子项的ForceText属性随时覆盖该控件,该属性会覆盖一切,并显示Text属性中的内容。

// add a progress bar control to a sub item
// setting item 0 and subitem 0

ProgressBar pb = new ProgressBar();
pb.Value = 50; // set it to some arbitrary value
item[0].SubItems[0].Control = pb;

激活嵌入

上图显示了一个激活的嵌入式文本框,使此单元格可编辑。

激活嵌入是两种嵌入类型中最有用的,也稍微复杂一些。激活嵌入允许您嵌入一个控件,而无需真正嵌入该控件。激活的嵌入控件只有在有人双击单元格时才会显示。您在列定义中设置激活的嵌入控件。

我尝试了许多不同的方法来实现激活嵌入,直到我提出了要求实现GLActivatedEmbedded接口的系统。要使用激活嵌入类型,您需要进入列区域并进行相应设置。激活嵌入类型对整个列有效。每列不能有一种以上类型,也不能将激活嵌入与标准控件嵌入混合。

// Add a column, then set its embedded type
GLColumn column = this.glacialList2.Columns.Add( "First column", 100 );
column.ActivatedEmbeddedType = GLActivatedEmbeddedTypes.TextBox;

或者通过列集合编辑器中列的类型设置。

如果您想使用自己的类型作为激活嵌入控件,则需要将激活嵌入类型设置为UserType。然后,您需要将GLActivatedEmbedded接口添加到您的控件并实现其成员。例如,这是文本框控件的实现。

// snipet from my textbox built in implementation
// of the activated embedded control
protected GLItem m_item = null;
protected GLSubItem m_subItem = null;
protected GlacialList m_Parent = null;

// called when control is activated
public bool GLLoad( GLItem item, GLSubItem subItem, GlacialList listctrl )
{
    this.BorderStyle = BorderStyle.None;
    this.AutoSize = false;

    m_item = item;
    m_subItem = subItem;
    m_Parent = listctrl;

    this.Text = subItem.Text;

    return true;
}

// called when control is to be destroyed
public void GLUnload()
{
    m_subItem.Text = this.Text;
}


// form1.cs
column.ActivatedEmbeddedControlType = new GLTextBox();

此时,该列中的每个单元格都可以使用激活嵌入控件了!

XP标题样式

为了使控件具有XP外观,您需要执行两项操作。首先,您需要将ControlStyle设置为XP。其次,您需要在应用程序的开头放置Application.EnableVisualStyles();

超平样式

这是我为了满足需要非常轻量级报表的需求而创建的一种样式。如您所见,您还可以设置交替颜色字段以使其更加生动。

设计时

我喜欢VS.NET的一大优点是其设计时支持。该框架的一个好部分是CollectionEditor。集合编辑器允许您添加/删除/编辑集合项以及编辑它们的属性。

要在设计时为集合添加集合编辑器支持,您需要执行几个步骤。

  1. 为集合中保存的基础类型创建一个类型转换器。
    public class GLColumnConverter : TypeConverter 
    {
      public override bool CanConvertTo(ITypeDescriptorContext context, 
                                        Type destinationType)
      {
        if (destinationType == typeof(InstanceDescriptor)) 
          return true;
        return base.CanConvertTo(context, destinationType);
      }
      public override object ConvertTo(ITypeDescriptorContext context, 
                                       CultureInfo culture, object value, 
                                       Type destinationType)
      {
        if (destinationType == typeof(InstanceDescriptor) 
                 && value is GLColumn)
        {
          GLColumn column = (GLColumn)value; 
          ConstructorInfo ci = 
                 typeof(GLColumn).GetConstructor(new Type[] {});
          if (ci != null)
            return new InstanceDescriptor(ci, null, false);
        }
        return base.ConvertTo(context, culture, value, destinationType); 
      } 
    }
  2. [TypeConverter("YourNameSpace.YourTypeConverter")]属性添加到您的集合类型。这将允许您的类型在设计时被序列化。
    [TypeConverter("GlacialComponents.Controls.GLColumnConverter")]
    public class GLColumn
    {
      ...
  3. 创建一个自定义集合编辑器类并添加刷新(否则您的设计时图形将不会更新新信息)。
    public class CustomCollectionEditor : CollectionEditor
    {
      public CustomCollectionEditor(Type type) : base(type) {}
      public override object EditValue(ITypeDescriptorContext context, 
                                       IServiceProvider isp, object value)
      {
        GlacialList originalControl = (GlacialList)context.Instance;
        object returnObject = base.EditValue( context, isp, value );
        originalControl.Refresh();//.Invalidate( true );
        return returnObject;
      }
    }
  4. 将Collection Editor属性添加到集合实例。
    [
    Category("Behavior"),
    Description("Column Collection"),
    DesignerSerializationVisibility(
      DesignerSerializationVisibility.Content),
      Editor(typeof(CustomCollectionEditor), typeof(UITypeEditor)),
    Browsable(true)
    ]
    public GLColumnCollection Columns
    {
        get    { return m_Columns; }
    }

这就是将设计时支持带入您的集合所需的一切!

一些常见问题解答

问:即使我设置正确,XP样式也没有显示。

答:您需要在应用程序的Application.Run(...)之前调用Application.EnableVisualStyles()

问:在可见列右侧的标题中看不到假按钮。

答:股票ListView中的假按钮对我来说一直没有意义。这不是bug,这就是我想要的。

问:如何使单元格可编辑?

答:激活嵌入。转到列定义,并将激活嵌入类型设置为TextBox

问:为什么您的垂直滚动条在标题底部停止?

答:为什么不呢?滚动条控制的是客户端区域,而不是标题。股票ListView在此实现中有误。 :-)

问:它不让我“添加”子项,我哪里做错了?

答:这是我的控件最喜欢的功能之一,即子项会在后台自动添加和删除。如果您添加了一个项或列,请放心,子项已经存在。

我未来的方向是什么?

这个项目未来将分成两个方向。首先,我打算制作一个可以商业销售的“专业”版本。我打算根据我的学习重写大部分控件,并称之为2.0版本。但是,我也打算保留这个代码库,创建一个1.4及以上版本,它将继续在The Code Project上免费提供。我将通过向后移植我打算添加到2.0的主要功能(如TreeView)来实现这一点。我希望在我进入控件业务的同时,能得到大家的支持,并继续为大家提供免费控件。我在The Code Project上发布的版本将始终是免费且对所有人开放的。

对于v1.4,我希望在控件中加入一个树视图。我不确定是将其集成还是将其作为单独的控件(最有可能)来继承ListView。但这是我接下来要做的重点。我还需要真正地优化代码。现在这个列表控件的功能太多了,以至于每次进行更改时我都无法测试所有功能。请务必发送bug报告给我,我需要它们来改进这个控件。

结论

我对这个控件的第四次迭代真正实现了我一直想要的列表控件。它非常灵活、快速,并且具有大量功能。我最近将其集成到我的完整Glacial Source Control版本控制系统中,这也是我开始这个项目的初衷之一,并且进行得非常顺利。我特别关注bug和功能请求。但我希望您在我的消息板上提出功能请求,以便其他人可以评论。希望您喜欢这个控件。

许可证

您可以在个人和商业应用程序中使用此版本的ListView控件。但是,您只能以编译形式(修改或未修改)重新分发此控件,不得重新分发源代码或修改后的源代码。使用此控件时,请在关于页面或文档中包含“Glacial ListView - Copyright Glacial Components Software 2004 - http://www.glacialcomponents.com/”的引用。这将对我有所帮助,并使我能够继续为您提供免费控件。

历史

  • 2004年2月24日:v1.30 激活嵌入、功能
  • 2003年11月6日:v1.21 Bug修复
  • 2003年10月25日:v1.2 嵌入、功能
  • 2003年7月10日:v1.1 优化。
  • 2003年4月25日:v1.0 初始发布,包含基本功能。
© . All rights reserved.