Outlook样式分组列表控件

Screenshot - ArticleImage.gif

介绍

从CodeProject一开始,我就以各种形式加入了它,并且想要发表一篇文章已经有一段时间了,这是一个很棒的站点,我很高兴我终于可以回馈一些东西了。

我在开发一个名为FeedGhost的RSS阅读器时构建了Superlist控件。虽然有很多可用的商业分组列表控件,但我希望完全控制代码及其可用性。超级列表支持拖放列定制,分组以及处理数千项顺利。如果你想改变它的外观和感觉,它也是高度可定制的。在本文中,我将解释如何在演示项目中使用和扩展控件。如果您下载了源代码,您可以在Tests/SuperListTest目录下找到演示项目。

背景

在决定开发我自己的列表控件之前,我花了几个星期的时间与标准列表视图进行斗争,试图让它按照我想要的方式工作,但我最终放弃了,因为我不能以分组的视觉顺序获得选中的项目。我们在FeedGhost中需要这个,这样用户多选择的文章将在相应的HTML视图中以相同的顺序显示。

我决定从头开始编写控件,而不是以网格控件为基础。在以前的项目中,我工作过,我看到了网格弯曲和翘曲,以做其创造者的投标,只是一个维护磨石周围的项目脖子后;没有完全按照你想要的方式工作,有大量的补丁代码来获得它。最终,你会得到一堆人们不敢碰的脆弱代码。

我用了两周的时间开发了Superlist,这差不多是我花在Listview工作上的时间,不可否认的是,我花了接下来的两周来修复bug

使用的代码

首先要注意的是,我没有对这个控件进行任何窗体设计器兼容性工作。我倾向于使用设计器来布局控件,然后直接进入代码来完成其余的工作,因此在添加列和配置列等方面没有设计器支持Superlist。要使用控件,你当然需要添加它到一个表单或用户控件,然后在代码中,你将需要创建它的列如下例:

public SuperListTestForm()
{
    InitializeComponent();
    Column surnameColumn = new Column( "surname", "Surname", 120, 
        delegate( object item ) { return ((Person)item).Surname; } );
    Column firstnameColumn = new Column( "firstname", "Firstname", 120, 
        delegate( object item ) { return ((Person)item).Firstname; } );
    Column phoneColumn = new Column( "phone", "Phone", 100, delegate( 
        object item ) { return ((Person)item).Phone; } );
    Column cityColumn = new Column( "city", "City", 60, delegate( 
        object item ) { return ((Person)item).City; } );
    Column stateColumn = new Column( "state", "State", 70, delegate( 
        object item ) { return ((Person)item).State; } );
    Column dateColumn = new Column( "date", "Date", 110, delegate( 
        object item ) { return ((Person)item).Date.ToString(); } );
    dateColumn.GroupItemAccessor = new ColumnItemValueAccessor( 
        GroupValueFromItem );
    dateColumn.MoveBehaviour = Column.MoveToGroupBehaviour.Copy;

    dateColumn.GroupSortOrder = SortOrder.Descending;
    surnameColumn.SortOrder = SortOrder.Ascending;

    _superList.Columns.Add( firstnameColumn );
    _superList.Columns.Add( phoneColumn );
    _superList.Columns.Add( stateColumn );
    _superList.Columns.Add( cityColumn );
    _superList.Columns.Add( dateColumn );
    _superList.Columns.GroupedItems.Add( dateColumn );
    _superList.Columns.GroupedItems.Add( stateColumn );

    _superList.SelectedItems.DataChanged += 
    new SelectedItemsCollection.DataChangedHandler( 
        SelectedItems_DataChanged );

    int tickStart = Environment.TickCount;
    const int iterationCount = 1; // Change this if you want to increase 
                                  // the number of items in the list
    for( int i = 0; i < iterationCount; i++ ) 
    {
        _superList.Items.AddRange( Person.GetData() );
    }
}

列对象

列对象是你的大部分工作在获得控制和运行方面,你创建它与以下构造函数:

public Column( string name, string caption, int width, 
    ColumnItemValueAccessor columnItemValueAccessor )

name参数用于唯一地标识要序列化的列,等等。参数columnItemValueAccessor是一个你需要提供的委托,用于返回在关联的单元格中呈现的对象(通常是一个字符串):

public delegate object ColumnItemValueAccessor( object rowItem );

一旦定义了列,就可以通过columns属性将它们添加到列表中。

分组

分组就像将列添加到列中一样简单。GroupedItems财产。默认情况下,用于分组的值与在列构造函数中传递的columnItemValueAccessor参数相同。但是,您可以通过向列提供一个新的ColumnItemValueAccessor委托来覆盖它。GroupItemAccessor财产。在我的示例程序中,我覆盖了这个属性来提供分组日期列值‘Today’,‘Yesterday’,‘Last Week’等等。

排序

默认情况下,columnItemValueAccessor参数返回的值必须支持icom寓言,否则在应用排序时将抛出异常。或者,您可以重写列。Comparitor和列。如果您想手动处理比较,可以使用GroupComparitor。您可以通过设置列来设置列的初始排序样式。排序方式属性。还有一个列。GroupSortOrder属性,用于在分组模式下设置组排序。

行为

在过去,我看到的性能瓶颈之一就是应用程序在控件中添加了很多项,导致它在视觉上变慢,因为控件每次添加新项时都会更新自己。这些问题很容易解决,通常需要在添加操作进行时告诉控件禁用呈现。当超级列表在后台处理列表更改时,我们绕过了这些潜在的问题,如果你想让UI直接与更改同步,那么你可以调用ListControl.Items.SynchroniseWithUINow()。默认情况下,后台处理是在应用程序空闲时完成的,您可以通过设置ListControl.Items在单独的线程中更改这一点,从而使部分处理在单独的线程中完成。ProcessingStyle = BinaryComponents.SuperList.ItemLists.ProcessingStyle。线程,这将把处理的排序部分转移到线程中。在设置线程模式时,列对象上的任何属性都可能在单独的线程中调用。

设计

控件的每个可视化方面,如标题、行和单元格都派生自Section, Section在语义上类似于control,但没有后者的资源开销。它具有矩形区域、焦点、鼠标和拖放支持。

Screenshot - SectionsDiagram.png

section由SectionContainer对象包含,该对象将键盘、鼠标和拖放信息传递给它们。在超列表的情况下,列表控件(不在上面的图中)从SectionContainerControl派生,当列表控件被构造时,它将CustomiseListSection和ListSection添加到它的画布上。CustomiseListSection包含分组列以及用于列表命令的工具条。ListSection包含列表标题、组和行。

定制

列表控件公开属性列表控件。SectionFactory,当用您自己的SectionFactory设置时,您可以覆盖任何列表部分。我们在FeedGhost中使用这个给列表一个光滑的外观(点击图片看大图):

Screenshot - FGScreenShot.jpg

重写section时,您感兴趣的两个主要方法是void section。布局(GraphicsSettings gs, Size maximumSize)和void Section。绘制(GraphicsSettings gs, Rectangle clipRect)。当父部分想要知道你的部分的大小和高度时,将调用布局。要在我的演示中看到一个覆盖行,如果你点击“自定义”菜单项,然后点击“Toggle row Paint Override”,你可以看到行是渐变填充的。代码可以看到下面从演示应用程序表格:

#region Example of overriding rows 
/// <spanclass="code-SummaryComment"><summary/></span>
/// Storage area for the row override.
/// <spanclass="code-SummaryComment"></summary/></span>
private RowOverrideExample _rowOverride;

private void toggleRowPaintingOverrideToolStripMenuItem_Click( object sender, 
    EventArgs e )
{
    if( _rowOverride == null )
    {
        //
        // Start overrride.
        _rowOverride = new RowOverrideExample( _superList );
    }
    else
    {
        //
        // Clear override.
        _rowOverride.Dispose();
        _rowOverride = null;
    }
}
/// <spanclass="code-SummaryComment"><summary/></span>
/// Example of overriding rows giving a gradient fill look.
/// <spanclass="code-SummaryComment"></summary/></span>
private class RowOverrideExample: IDisposable
{
    public RowOverrideExample( 
        BinaryComponents.SuperList.ListControl listControl )
    {
        _oldFactory = listControl.SectionFactory; // store old factory as we
                                                 // want to leave as we came.
        _listControl = listControl;

        //
        // Replace the current SectionFactory with our override.
        listControl.SectionFactory = new MySectionFactory(); //
        _listControl.LayoutSections();
    }
    public void Dispose()
    {
        if( _oldFactory != null ) // put things back as they were
        {
            _listControl.SectionFactory = _oldFactory;
            _listControl.LayoutSections();
        }
    }
    private class MySectionFactory : SectionFactory
    {
        public override RowSection CreateRowSection( 
            BinaryComponents.SuperList.ListControl listControl, 
            RowIdentifier rowIdenifier, 
            HeaderSection headerSection, 
            int position )
        {
            return new MyRowSection( listControl, rowIdenifier, 
                headerSection, position );
        }
    }
    private class MyRowSection : RowSection
    {
        public MyRowSection( 
            BinaryComponents.SuperList.ListControl listControl, 
            RowIdentifier rowIdentifier, 
            HeaderSection headerSection, 
            int position )
            : base( listControl, rowIdentifier, headerSection, position )
        {
            _position = position;
        }

        public override void PaintBackground( Section.GraphicsSettings gs, 
            Rectangle clipRect )
        {
            Color from, to;
            if( _position % 2 == 0 )
            {
                from = Color.White;
                to = Color.LightBlue;
            }
            else
            {
                to = Color.White;
                from = Color.LightBlue;
            }
            using( LinearGradientBrush lgb = new LinearGradientBrush( 
                this.Rectangle, 
                from, 
                to,
                LinearGradientMode.Horizontal ) )
            {
                gs.Graphics.FillRectangle( lgb, this.Rectangle );
            }
        }
        public override void Paint( Section.GraphicsSettings gs, 
            Rectangle clipRect )
        {
            base.Paint( gs, clipRect );
        }
        private int _position;
    }

    private BinaryComponents.SuperList.ListControl _listControl;
    private SectionFactory _oldFactory;
}
#endregion

在定制部分有一个工具条,它是在ToolStripOptionsToolbarSection对象中创建的:

Screenshot - ToolStripOptionsToolbarSection.png

你也可以通过覆盖ToolStripOptionsToolbarSection类并通过你自己的SectionFactory传入你的版本来添加你自己的toolstripitem到它。或者,你可以替换整个区域,如果你想通过OptionsToolbarSection派生;我们在FeedGhost中使用我们自己的带状带代替。

加载和保存状态

Superlist的状态可以用void列表控件保存和加载。SerializeState(先。TextWriter writer)和void列表控件。DeSerializeState(先。TextReader阅读器)分别。

储蓄的例子

using( System.IO.TextWriter textWriter = File.CreateText( ofd.FileName ) )
{
    _superList.SerializeState( textWriter );
}

加载示例

using( System.IO.TextReader textReader = File.OpenText( fileName ) )
{
    _superList.DeSerializeState( textReader );
}

包装起来

我希望你发现这个控制有用,如果你有任何想法,bug或建议,请在这里留言。我还将在这里发布更新,以及在我的公司网站二进制组件。

本文转载于:http://www.diyabc.com/frontweb/news13584.html

原文地址:https://www.cnblogs.com/Dincat/p/13477674.html