1. 程式人生 > 實用技巧 >Outlook樣式分組列表控制元件

Outlook樣式分組列表控制元件

介紹

從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,但沒有後者的資源開銷。它具有矩形區域、焦點、滑鼠和拖放支援。

section由SectionContainer物件包含,該物件將鍵盤、滑鼠和拖放資訊傳遞給它們。在超列表的情況下,列表控制元件(不在上面的圖中)從SectionContainerControl派生,當列表控制元件被構造時,它將CustomiseListSection和ListSection新增到它的畫布上。CustomiseListSection包含分組列以及用於列表命令的工具條。ListSection包含列表標題、組和行。

定製

列表控制元件公開屬性列表控制元件。SectionFactory,當用您自己的SectionFactory設定時,您可以覆蓋任何列表部分。我們在FeedGhost中使用這個給列表一個光滑的外觀(點選圖片看大圖):

重寫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物件中建立的:

你也可以通過覆蓋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