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