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

所有者绘制控件 - 可扩展 ListBox

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (27投票s)

2007年3月8日

CPOL

4分钟阅读

viewsIcon

95634

downloadIcon

3908

这是关于自绘控件系列文章的第一篇,重点介绍 ListBox 控件。本文包含了基本功能和一些有用的技巧,帮助您开始开发自己的自绘控件。

Screenshot

引言

这是自绘控件系列教程的第一篇。其目的不是创建一个带有大量花哨功能的控件,这留给您自己的想象力,而是演示一种创建自己的自绘 ListBox 控件的方法。

此控件与我在 CP 上看到的其他派生 ListBox 控件不同,因为它允许项目处于折叠或扩展状态。换句话说,当您单击一个项目时,它会切换项目视图,使其可以最小化或最大化,从而允许在最大化时查看其他信息。在截图上,“Just Chill Man!”项目已展开,所有其他项目都已折叠。

对于此演示,我创建了一个 ExtendedListBoxItem 类,该类派生自用于维护项目信息的对象,并包含 DrawCollapsedDrawExtendedDrawBorderAddGlow 方法。这个类只是一个模板,用于展示可能性以及构建您自己的项目类的起点。我相信,一旦掌握了这些技能,您将能够创建一个更大、更好、更棒的 ListBoxItemControl 类,以满足您的特定需求。

入门

ExtendedListBoxControl 派生自 System.Windows.Forms.ListBox 控件,设置了以下属性

ExtendedListBoxControl.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;

这就是允许我们自己绘制项目的原因。将 DrawMode 设置为 OwnerDrawVariable 意味着项目可以是任何我们想要的大小,最大为 255 像素。

此外,我们必须重写两个虚拟方法才能实际进行绘制

  • OnMeasureItem - 确定项目的大小。
  • OnDrawItem - 实际绘制项目的方法。

我们将要重写的另一个方法是

  • OnMouseDown - 捕获鼠标按下事件。

下一节将更详细地描述这三种方法。

重写

如前所述,我们必须重写两个虚拟方法才能绘制我们自己的控件。第一个是 OnMeasureItem 方法。调用此方法以确定绘制项目的大小。在我们的例子中,它取决于项目是处于折叠状态还是扩展状态。这些值由 MinSize(折叠大小)和 MaxSize(扩展大小)属性确定。

/// 
/// Called when invalidated to find the item bounds. Since we are only concerned 
/// with the height we only need to set this value and leave the bounds.width 
/// as is! 
///
protected override void OnMeasureItem(System.Windows.Forms.MeasureItemEventArgs e) 
{
    base.OnMeasureItem(e); 

    if ((e.Index < 0) || (e.Index >= itemCache.Count))
        return; 

    ExtendedListBoxItem xlbi = (ExtendedListBoxItem) itemCache[e.Index];

    //If its the current selection and not collapsed 
    // set height to Max. otherwise just set the item selected to Min. value.
    if ((e.Index == _currentIndex) && !isCollapsed) 
        e.ItemHeight = xlbi.MaxSize; 
    else
        e.ItemHeight = xlbi.MinSize; 

    e.ItemWidth = this.Width; 
}

我们终于得到了最重要的方法,即实际进行绘制的方法。此方法使用 isCollapsed 属性来确定是绘制折叠状态的项目还是扩展状态的项目。

///
/// Called when the item needs to be drawn
///    
protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
{
    base.OnDrawItem(e);

    
    //If not a valid index just ignore      
    if ((e.Index < 0) || (e.Index >= itemCache.Count))
        return;

    ExtendedListBoxItem xlbi = (ExtendedListBoxItem) itemCache[e.Index];

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;

    
    //Draw the item in current state.
    if ((e.Index == _currentIndex) && !isCollapsed)
        xlbi.DrawExpanded(e);
    else
        xlbi.DrawCollapsed(e);
}

项目的绘制委托给项目本身;通过这种方式,您可以使用基类提供基本功能,创建自己的类或从 ExtendedLstBoxItem 派生一个类,并使用其全部或部分功能来满足您的需求。

索引值通过 DrawItemEventArgs 传递。我们使用此索引来检索对项目的引用,然后调用其相应的 DrawXXXX 方法来完成这项工作!

接下来,我们重写 OnMouseDown 而不是 OnSelectedIndexChanged,因为后者仅在用户单击与所选索引不同的索引时才被调用,并且我们需要捕获鼠标按下事件,无论选择了哪个项目。当尝试使列表项无效时,我遇到了一个问题。当尝试展开或折叠项目时,没有调用 OnMeasureItem 处理程序。为了解决这个问题,我搜索了 Google,并找到了一篇晦涩的文章,该文章建议必须先删除要无效的项目,然后再将其重新插入到列表中。以下代码对 Previous 和 Current 项目执行此操作

protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e) 
{
    base.OnMouseDown(e); 
    
        :
        :
    
    //Invalidate previous selection 
    InvalidateItem(_previousIndex); 
    
    //Invalidate current selection 
    InvalidateItem(_currentIndex); 
}

/// 
/// Invalidates the item at the provided index.
///
public void InvalidateItem(int index) 
{
    if ((index < 0) || (index >= itemCache.Count)) 
        return; 

    //All we need to do here is make sure
    //we get the correct item index.
    this.Items.RemoveAt(index); 
    this.Items.Insert(index, " "); 
}

关注点

在运行演示时,我提供了一个 PropertyGrid,以便可以直观地设计项目。此功能非常方便,因为它允许您使用不同的属性,并查看发生了什么事情,而无需在每次更改时都重新编译。感谢 Microsoft 提供了这个方便的控件。

注意:MinSizeMaxSize 属性不会立即生效;您必须切换项目才能反映更改。

希望这篇文章对您有所帮助!

© . All rights reserved.