using System;
using System.Text;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.IO;
using System.Windows.Forms;
using System.Diagnostics;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Collections.Specialized;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.Reflection;
namespace GlacialComponents.Controls
{
#region Helper Classes
public enum ChangedTypes { GeneralInvalidate, SubItemChanged, ItemChanged, ItemCollectionChanged, ColumnChanged, ColumnCollectionChanged };
public enum Alignments { alignLeft, alignCenter, alignRight }
public class ChangedEventArgs : EventArgs
{
private int m_nItem = -1;
private int m_nColumn = -1;
private ChangedTypes m_ctType = ChangedTypes.GeneralInvalidate;
public ChangedEventArgs( ChangedTypes ctType, int nColumn, int nItem )
{
m_nItem = nItem;
m_nColumn = nColumn;
m_ctType = ctType;
}
public int Item
{
get { return m_nItem; }
}
public int Column
{
get { return 0; }
}
public ChangedTypes ChangedType
{
get { return m_ctType; }
}
}
public class ClickEventArgs : EventArgs
{
private int m_nItemIndex;
private int m_nColumnIndex;
public ClickEventArgs( int itemindex, int columnindex )
{
m_nItemIndex = itemindex;
m_nColumnIndex = columnindex;
}
public int ItemIndex
{
get
{
return m_nItemIndex;
}
}
public int ColumnIndex
{
get
{
return m_nColumnIndex;
}
}
}
public class ManagedHScrollBar : System.Windows.Forms.HScrollBar
{
public int mTop
{
set
{
if ( Top!=value)
Top = value;
}
}
public int mLeft
{
set
{
if ( value != Left )
Left = value;
}
}
public int mWidth
{
get
{
if ( Visible != true )
return 0;
else
return Width;
}
set
{
if ( Width != value )
Width = value;
}
}
public int mHeight
{
get
{
if ( Visible != true )
return 0;
else
return Height;
}
set
{
if ( Height != value )
Height = value;
}
}
public bool mVisible
{
set
{
if ( Visible != value )
Visible = value;
}
}
public int mSmallChange
{
set
{
if ( SmallChange != value )
SmallChange = value;
}
}
public int mLargeChange
{
set
{
if ( LargeChange != value )
LargeChange = value;
}
}
public int mMaximum
{
set
{
if ( Maximum != value )
Maximum = value;
}
}
}
public class ManagedVScrollBar : System.Windows.Forms.VScrollBar
{
public int mTop
{
set
{
if ( Top!=value)
Top = value;
}
}
public int mLeft
{
set
{
if ( value != Left )
Left = value;
}
}
public int mWidth
{
get
{
if ( Visible != true )
return 0;
else
return Width;
}
set
{
if ( Width != value )
Width = value;
}
}
public int mHeight
{
get
{
if ( Visible != true )
return 0;
else
return Height;
}
set
{
if ( Height != value )
Height = value;
}
}
public bool mVisible
{
set
{
if ( Visible != value )
Visible = value;
}
}
public int mSmallChange
{
set
{
if ( SmallChange != value )
SmallChange = value;
}
}
public int mLargeChange
{
set
{
if ( LargeChange != value )
LargeChange = value;
}
}
public int mMaximum
{
set
{
if ( Maximum != value )
Maximum = value;
}
}
}
#endregion
/// <summary>
/// Summary description for GlacialList.
/// </summary>
public class GlacialList : System.Windows.Forms.UserControl
{
#region Debugging
public static void DW( string strout ) // debug write
{
#if false
//System.IO.StreamWriter sw = new System.IO.StreamWriter( "e:\\debug.txt", true );
//sw.WriteLine( strout );
//sw.Close();
#else
Debug.WriteLine( strout );
#endif
}
#endregion
#region Header
#region Events and Delegates
#region Clicked Events
public delegate void ClickedEventHandler( object source, ClickEventArgs e );//int nItem, int nSubItem );
public event ClickedEventHandler ItemClickedEvent;
public event ClickedEventHandler ColumnClickedEvent;
public event ClickedEventHandler RightClickedEvent;
#endregion
#region Changed Events
public delegate void ChangedEventHandler( object source, ChangedEventArgs e ); //int nItem, int nSubItem );
public event ChangedEventHandler ItemChangedEvent;
public event ChangedEventHandler ColumnChangedEvent;
#endregion
#endregion
#region VarsDefsProps
#region TestCode
void OnItemClicked( object source, ClickEventArgs e )// int nItem, int nSubItem )
{
DW( "OnItemClicked" );
}
#endregion
#region ClassVariablesAndEnums
//public enum CoordType { none, ColumnSelect, ItemSelect, ColumnResize };
public enum GridStyles { gridNone, gridDashed, gridSolid }
public enum ListStates { stateNone, stateSelecting, stateColumnSelect, stateColumnResizing }
public enum RedrawStates { RedrawNormal, RedrawSuspended, RedrawSuspendedPending }
//static int BorderSize = 2;
static int RESIZE_ARROW_PADDING = 2;
static int MINIMUM_COLUMN_SIZE = 10;
private int m_nLastSelectionIndex = 0;
private int m_nLastSubSelectionIndex = 0;
private ListStates m_nState = ListStates.stateNone;
private Point m_pointColumnResizeAnchor;
private int m_nResizeColumnNumber; // the column number thats being resized
private System.ComponentModel.Container components = null;
private GlacialComponents.Controls.ManagedVScrollBar vPanelScrollBar;
private GlacialComponents.Controls.ManagedHScrollBar hPanelScrollBar;
#endregion
#region ClassProperties
private GridStyles m_nGridLineStyle = GridStyles.gridSolid;
private int m_nItemHeight = 18;
private int m_nHeaderHeight = 18;
private int m_nBorderWidth = 2;
private Color m_colorGridColor = Color.Gray;
private bool m_bMultiSelect = false;
private Color m_colorSelectionColor = Color.SeaShell;
private RedrawStates m_RedrawState = RedrawStates.RedrawNormal; // true if we can redraw, false if we are holding redraw up
private bool m_bHeaderVisible = true;
private GLColumnCollection m_Columns;// = new GLColumnCollection( this );
private GLItemCollection m_Items;// = new GLItemCollection();
private int m_nMaxHeight = 0;
private bool m_bAutoHeight = true;
private bool m_bAllowColumnResize = true;
private bool m_bFullRowSelect = true;
#region Control Properties
[
Description("Allow resizing of columns"),
Category("Behavior"),
Browsable(true)
]
public bool AllowColumnResize
{
get { return m_bAllowColumnResize; }
set { m_bAllowColumnResize = value; }
}
[
Description("Do we want rows to automatically adjust height"),
Category("Behavior"),
Browsable(true)
]
public bool AutoHeight
{
get { return m_bAutoHeight; }
set { m_bAutoHeight = value; }
}
/// <summary>
/// you want the header to be visible or not
/// </summary>
[
Description("Column Headers Visible"),
Category("Behavior"),
Browsable(true)
]
public bool HeaderVisible
{
get { return m_bHeaderVisible; }
set { m_bHeaderVisible = value; }
}
[
Category("Behavior"),
Description("Column Collection"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
Editor(typeof(CustomCollectionEditor), typeof(UITypeEditor)),
Browsable(true)
]
public GLColumnCollection Columns
{
get { return m_Columns; }
}
[
Category("Behavior"),
Description("Items collection"),
Browsable(false)
]
public GLItemCollection Items
{
get { return m_Items; }
}
[
Description("Background color to mark selection."),
Category("Appearance"),
Browsable(true),
]
public Color SelectionColor
{
get { return m_colorSelectionColor; }
set { m_colorSelectionColor = value; }
}
[
Description("Allow full row select."),
Category("Behavior"),
Browsable(true)
]
public bool FullRowSelect
{
get { return m_bFullRowSelect; }
set { m_bFullRowSelect = value; }
}
[
Description("Allow multiple selections."),
Category("Behavior"),
Browsable(true)
]
public bool AllowMultiselect
{
get { return m_bMultiSelect; }
set { m_bMultiSelect = value; }
}
[
Description("Border Padding"),
Category("Appearance"),
Browsable(true),
DefaultValue(2)
]
public int BorderPadding
{
get { return m_nBorderWidth; }
set { m_nBorderWidth = value; }
}
[
Description("Whether or not to draw gridlines"),
Category("Grid"),
Browsable(true),
DefaultValue(0)
]
public GridStyles GridLines
{
get { return m_nGridLineStyle; }
set
{
m_nGridLineStyle = (GridStyles)value;
ManagedInvalidate();
}
}
[
Description("Color of the grid if we draw it."),
Category("Grid"),
Browsable(true)
]
public Color GridColor
{
get { return m_colorGridColor; }
set
{
m_colorGridColor = (Color)value;
ManagedInvalidate();
}
}
/// <summary>
/// how big do we want the individual items to be
/// </summary>
[
Description("How high each row is."),
Category("Appearance"),
Browsable(true)
]
public int ItemHeight
{
get { return m_nItemHeight; }
set
{
m_nItemHeight = value;
ManagedInvalidate();
}
}
[
Description("How high the columns are."),
Category("Appearance"),
Browsable(true)
]
public int HeaderHeight
{
get
{
if ( HeaderVisible == true )
return m_nHeaderHeight;
else
return 0;
}
set
{
m_nHeaderHeight = value;
ManagedInvalidate();
}
}
/// <summary>
/// amount of space inside any given cell to borders
/// </summary>
[
Description("Cell padding area"),
Browsable(false)
]
public int CellPaddingSize
{
get { return 4; } // default I set to 4
}
#endregion
#region Working Properties
/// <summary>
/// are we currently allowing redraw?
/// </summary>
[
Description("To redraw when invalidate is called or not."),
Browsable(false)
]
public RedrawStates RedrawState
{
get { return m_RedrawState; }
set
{
if ( (m_RedrawState==RedrawStates.RedrawSuspendedPending) && (value==RedrawStates.RedrawNormal) )
Invalidate( true ); // changing back to normal, flush the invlidate we been holding onto
if ( (m_RedrawState==RedrawStates.RedrawSuspendedPending) && (value==RedrawStates.RedrawSuspended) )
return;
m_RedrawState = value;
}
}
[
Description("Number of items/rows in the list."),
Category("Behavior"),
Browsable(false),
DefaultValue(0)
]
public int Count
{
get { return Items.Count; }
}
[
Description("All items together height."),
Browsable(false)
]
public int TotalRowHeight
{
get
{
return ItemHeight * Items.Count;
}
}
[
Description("Number of rows currently visible in inner rect."),
Browsable(false)
]
public int VisibleRows
{
get { return RowsInnerClientRect.Height / ItemHeight; }
}
[
Description("this will always reflect the most height any item line has needed"),
Browsable(false)
]
public int MaxHeight
{
get { return m_nMaxHeight; }
set
{
if ( value > m_nMaxHeight )
{
m_nMaxHeight = value;
if ( AutoHeight == true )
{
ItemHeight = MaxHeight;
ManagedInvalidate();
DW("Item height set bigger");
}
}
}
}
[
Description("The rectangle of the header inside parent control"),
Browsable(false)
]
public Rectangle HeaderRect
{
get { return new Rectangle( this.BorderPadding, this.BorderPadding, Width-(this.BorderPadding*2), HeaderHeight ); }
}
[
Description("The rectangle of the client inside parent control"),
Browsable(false)
]
public Rectangle RowsClientRect
{
get
{
int tmpY = HeaderHeight + BorderPadding; // size of the header and the top border
int tmpHeight = Height - HeaderHeight - (BorderPadding*2);
return new Rectangle( BorderPadding, tmpY, Width-(this.BorderPadding*2), tmpHeight );
}
}
[
Description("The inner rectangle of the client inside parent control taking scroll bars into account."),
Browsable(false)
]
public Rectangle RowsInnerClientRect
{
get
{
Rectangle innerRect = RowsClientRect;
innerRect.Width -= vPanelScrollBar.mWidth; // horizontal bar crosses vertical plane and vice versa
innerRect.Height -= hPanelScrollBar.mHeight;
if ( innerRect.Width < 0 )
innerRect.Width = 0;
if ( innerRect.Height < 0 )
innerRect.Height= 0;
return innerRect;
}
}
#endregion
#endregion
#endregion
#endregion
#region Implementation
#region Initialization
public GlacialList()
{
DW("Constructor");
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
m_Columns = new GLColumnCollection();
m_Columns.ChangedEvent += new GLColumnCollection.ChangedEventHandler( Columns_Changed ); // listen to event changes inside the item
m_Items = new GLItemCollection( this );
m_Items.ChangedEvent += new GLItemCollection.ChangedEventHandler( Items_Changed );
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.hPanelScrollBar = new GlacialComponents.Controls.ManagedHScrollBar();
this.vPanelScrollBar = new GlacialComponents.Controls.ManagedVScrollBar();
this.SuspendLayout();
//
// hPanelScrollBar
//
this.hPanelScrollBar.Anchor = System.Windows.Forms.AnchorStyles.None;
this.hPanelScrollBar.CausesValidation = false;
this.hPanelScrollBar.Location = new System.Drawing.Point(12, 128);
this.hPanelScrollBar.mHeight = 16;
this.hPanelScrollBar.mWidth = 120;
this.hPanelScrollBar.Name = "hPanelScrollBar";
this.hPanelScrollBar.Size = new System.Drawing.Size(120, 16);
this.hPanelScrollBar.TabIndex = 3;
this.hPanelScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hPanelScrollBar_Scroll);
//
// vPanelScrollBar
//
this.vPanelScrollBar.Anchor = System.Windows.Forms.AnchorStyles.None;
this.vPanelScrollBar.CausesValidation = false;
this.vPanelScrollBar.Location = new System.Drawing.Point(132, 8);
this.vPanelScrollBar.mHeight = 120;
this.vPanelScrollBar.mWidth = 16;
this.vPanelScrollBar.Name = "vPanelScrollBar";
this.vPanelScrollBar.Size = new System.Drawing.Size(16, 120);
this.vPanelScrollBar.TabIndex = 4;
this.vPanelScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vPanelScrollBar_Scroll);
//
// GlacialList
//
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.vPanelScrollBar,
this.hPanelScrollBar});
this.Name = "GlacialList";
this.Size = new System.Drawing.Size(160, 152);
this.Resize += new System.EventHandler(this.GlacialList_Resize);
this.Load += new System.EventHandler(this.GlacialList_Load);
this.MouseUp += new System.Windows.Forms.MouseEventHandler(this.GlacialList_MouseUp);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.GlacialList_Paint);
this.MouseMove += new System.Windows.Forms.MouseEventHandler(this.GlacialList_MouseMove);
this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.GlacialList_MouseDown);
this.ResumeLayout(false);
}
#endregion
private void GlacialList_Load(object sender, System.EventArgs e)
{
DW("GlacialList_Load");
}
#endregion
#region Event Handlers
protected void Items_Changed( object source, ChangedEventArgs e )
{
DW("GlacialList::Items_Changed");
if ( ItemChangedEvent != null )
ItemChangedEvent( this, e ); // fire the column clicked event
ManagedInvalidate();
}
public void Columns_Changed( object source, ChangedEventArgs e )
{
DW("Columns_Changed");
if ( ColumnChangedEvent != null )
ColumnChangedEvent( this, e ); // fire the column clicked event
ManagedInvalidate();
}
#endregion
#region HelperFunctions
public void ManagedInvalidate()
{
DW("Managed refresh (invalidate)");
if ( ( RedrawState == RedrawStates.RedrawSuspended ) || (RedrawState == RedrawStates.RedrawSuspendedPending) )
RedrawState = RedrawStates.RedrawSuspendedPending;
else
Invalidate( true );
}
protected void InterpretCoords( int nScreenX, int nScreenY, out int nItem, out int nColumn, out ListStates nState )
{
DW("Interpret Coords");
nState = ListStates.stateNone;
nColumn = 0; // compiler forces me to set this since it sometimes wont get set if routine falls through early
nItem = 0;
/*
* Calculate horizontal subitem
*/
int nCurrentX = -hPanelScrollBar.Value; //GetHScrollPoint(); // offset the starting point by the current scroll point
//int nColIndex = 0;
foreach ( GLColumn col in Columns )
{
if ( (nScreenX > nCurrentX) && (nScreenX < (nCurrentX+col.Width-RESIZE_ARROW_PADDING)) )
{
//nColumn = nColIndex;
nState = ListStates.stateColumnSelect;
break;
}
if ( (nScreenX > (nCurrentX+col.Width-RESIZE_ARROW_PADDING)) && (nScreenX < (nCurrentX+col.Width+RESIZE_ARROW_PADDING)) )
{
//nColumn = nColIndex;
if ( AllowColumnResize == true )
nState = ListStates.stateColumnResizing;
return; // no need for this to fall through
}
nColumn++;
nCurrentX += col.Width;
}
if ( ( nScreenY >= RowsInnerClientRect.Y ) && ( nScreenY < RowsInnerClientRect.Bottom ) )
{ // we are in the client area
nItem = ((nScreenY - RowsInnerClientRect.Y) / ItemHeight) + vPanelScrollBar.Value;
if ( nItem >= Items.Count )
nState = ListStates.stateNone;
else
{
nState = ListStates.stateSelecting;
// handle case of where FullRowSelect is OFF and we click on the second part of a spanned column
for ( int nSubIndex = 0; nSubIndex < Columns.Count; nSubIndex++ )
{
if ( ( nSubIndex + (Items[nItem].SubItems[nSubIndex].Span-1) ) >= nColumn )
{
nColumn = nSubIndex;
return;
}
}
}
return;
}
return;
}
public int GetColumnScreenX( int nColumn )
{
DW("Get Column Screen X");
if ( nColumn >= Columns.Count )
return 0;
int nCurrentX = -hPanelScrollBar.Value;//GetHScrollPoint(); // offset the starting point by the current scroll point
int nColIndex = 0;
foreach ( GLColumn col in Columns )
{
if ( nColIndex >= nColumn )
return nCurrentX;
nColIndex++;
nCurrentX += col.Width;
}
return 0; // this should never happen;
}
public static string TruncateString( string strText, int nWidth, Graphics subDC, Font font )
{
// THIS FUNCTION ALSO HANDLES TRUNCATION OF MULTILINE STRINGS
//DW("TuncateString");
string strTruncated = "";
Size sizeString = MeasureMultiLineString( strText, subDC, font );
if ( sizeString.Width < nWidth )
return strText; // this doesnt need any work, bail out
int strTDotSize;
strTDotSize = (int)subDC.MeasureString( "...", font ).Width;
if ( strTDotSize > nWidth )
return ""; // Cant even fit the triple dots here
StringReader r = new StringReader(strText);
string line;
while ((line = r.ReadLine()) != null)
{
if ( (int)subDC.MeasureString( line, font ).Width < nWidth )
{ // original sub line is fine, doesn't need truncation
strTruncated += line + "\n";
}
else
{ // sub line needs to be truncated
for ( int index=line.Length; index!=0; index-- )
{
string tmpString;
tmpString = line.Substring( 0, index ) + "...";
//DW("Truncating string to " + strText );
if ( (int)subDC.MeasureString( tmpString, font ).Width < nWidth )
{
strTruncated += tmpString + "\n";
break; // stop the for loop so we can test more strings
}
}
}
}
// remove the trailing linefeed for the last line in a sequence (because its not needed and woudl possibly mess things up
if ( strTruncated.Length > 1 )
strTruncated.Remove( strTruncated.Length-1, 1 );
return strTruncated;
}
public static Size MeasureMultiLineString( string strText, Graphics mDC, Font font )
{
StringReader r = new StringReader(strText);
Size strSize = new Size(0,0);
string line;
while ((line = r.ReadLine()) != null)
{
SizeF tsize = mDC.MeasureString( line, font );
strSize.Height += (int)tsize.Height;
if ( strSize.Width < (int)tsize.Width )
strSize.Width = (int)tsize.Width;
}
return strSize;
}
#endregion
#region Dimensions
private void GlacialList_Resize(object sender, System.EventArgs e)
{
DW("GlacialList_Resize");
RecalcScroll();
ManagedInvalidate();
}
#endregion
#region Drawing
protected override void OnPaintBackground(PaintEventArgs pevent)
{
DW("OnPaintBackground");
//Debug.WriteLine( "paint background" );
}
private void GlacialList_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
DW("GlacialList_Paint");
RecalcScroll();
Bitmap offScreenBmp = new Bitmap( this.Width, this.Height);
Graphics offScreenDC = Graphics.FromImage(offScreenBmp);
offScreenDC.FillRectangle( SystemBrushes.Control, this.ClientRectangle );
if ( Columns.Count > 0 )
{
int nInsideWidth;
if ( Columns.Width > HeaderRect.Width )
nInsideWidth = Columns.Width;
else
nInsideWidth = HeaderRect.Width;
/*
* draw header
*/
if ( HeaderVisible == true )
{
Bitmap bmpHeader = new Bitmap( Columns.Width, HeaderRect.Height);
DrawHeader( bmpHeader, new Size( HeaderRect.Width, HeaderRect.Height ) );
//compute the source rect to copy from the full header thats been drawn
Rectangle sourceRect = new Rectangle( hPanelScrollBar.Value, 0, nInsideWidth, HeaderRect.Height);
offScreenDC.DrawImage( bmpHeader, HeaderRect.X, HeaderRect.Y, sourceRect, GraphicsUnit.Pixel ); // Copy the the correct portion of the pad to the actual viewable screen offscreendc
bmpHeader.Dispose();
}
/*
* draw client area
*/
Bitmap bmpRows = new Bitmap( nInsideWidth, RowsInnerClientRect.Height );
DrawRows( bmpRows, new Size( nInsideWidth, RowsInnerClientRect.Height ) );
//compute the source rect to copy from the full rows thats been drawn
Rectangle sourceRowsRect = new Rectangle( hPanelScrollBar.Value, 0, nInsideWidth, RowsInnerClientRect.Height );
offScreenDC.DrawImage( bmpRows, RowsInnerClientRect.X, RowsInnerClientRect.Y, sourceRowsRect, GraphicsUnit.Pixel );
bmpRows.Dispose();
}
ControlPaint.DrawBorder3D( offScreenDC, this.ClientRectangle, System.Windows.Forms.Border3DStyle.Sunken ); // draw control border
e.Graphics.DrawImage(offScreenBmp, 0, 0); // Now transfer it all to the visible screen
offScreenBmp.Dispose();
offScreenDC.Dispose();
return;
}
public void DrawHeader( Bitmap bmpHeader, Size sizeHeader )
{
DW("DrawHeader");
if ( Columns.Count <= 0 )
return;
Graphics HeaderDC = Graphics.FromImage( bmpHeader );
HeaderDC.FillRectangle( SystemBrushes.Control, 0, 0, sizeHeader.Width, sizeHeader.Height );
// draw vertical lines first, then horizontal lines
int nCurrentX = 0;
foreach ( GLColumn column in Columns )
{
Bitmap bmpColumn = new Bitmap( column.Width, HeaderHeight );
DrawColumn( bmpColumn, new Size( column.Width, HeaderHeight ), column );
HeaderDC.DrawImage( bmpColumn, nCurrentX, 0 );
bmpColumn.Dispose();
nCurrentX += column.Width;
}
HeaderDC.Dispose();
}
public void DrawColumn( Bitmap bmpColumn, Size colSize, GLColumn column )
{
DW("DrawColumn");
Graphics ColumnDC = Graphics.FromImage( bmpColumn );
string strItemText;
strItemText = column.Name;
if ( column.State == GLColumn.ColumnStates.csNone )
ControlPaint.DrawBorder3D( ColumnDC, new Rectangle(0,0,colSize.Width,colSize.Height), System.Windows.Forms.Border3DStyle.Raised );
if ( column.State == GLColumn.ColumnStates.csPressed )
ControlPaint.DrawBorder3D( ColumnDC, new Rectangle(0,0,colSize.Width,colSize.Height), System.Windows.Forms.Border3DStyle.Sunken );
// calc alignment
int ty, tx, tw;
int nInteriorWidth;
ty = (colSize.Height-Font.Height)/2;
tw = (int)ColumnDC.MeasureString( column.Name, Font ).Width;
tx = 0;
nInteriorWidth = colSize.Width - (CellPaddingSize*2);
if ( nInteriorWidth < 4 )
return; // if the label is bigger than the space for it, then dont draw anything
switch ( column.Alignment )
{
case Alignments.alignLeft: // left justified
{
tx = BorderPadding;
break;
}
case Alignments.alignCenter: // center justified
{
tx = (column.Width-tw)/2;
break;
}
case Alignments.alignRight: // right justified
{
tx = column.Width - tw - BorderPadding;
break;
}
}
if ( tw > nInteriorWidth )
ColumnDC.DrawString( TruncateString( strItemText, nInteriorWidth, ColumnDC, Font ), Font, Brushes.Black, CellPaddingSize, ty );
else
ColumnDC.DrawString( strItemText, Font, Brushes.Black, tx + CellPaddingSize, ty );
}
public void DrawRows( Bitmap bmpRows, Size sizeRows )
{
DW("DrawRows");
Graphics RowsDC = Graphics.FromImage( bmpRows );
RowsDC.FillRectangle( Brushes.White, 0, 0, sizeRows.Width, sizeRows.Height );
// -optimization- so we dont make new row bmp's every time, just reuse this one
Bitmap bmpRow = new Bitmap( sizeRows.Width, ItemHeight ); // this bitmap is reuseable because all rows are same size always
// determine start item based on whether or not we have a vertical scrollbar present
int nStartItem; // which item to start with in this visible pane
if ( this.vPanelScrollBar.Visible == true )
nStartItem = this.vPanelScrollBar.Value;
else
nStartItem = 0;
/* Draw Rows */
int nYCursor = 0;
for (int nItem = 0; ((nItem < (VisibleRows+1) ) && ((nItem+nStartItem) < Items.Count )); nItem++ )
{ //Debug.WriteLine( "ItemCount " + Items.Count.ToString() + " Item Number " + nItem.ToString() );
DrawRow( bmpRow, new Size( sizeRows.Width, ItemHeight ), nItem+nStartItem ); //, ArrayList Item )
RowsDC.DrawImage( bmpRow, 0, nYCursor );
nYCursor += ItemHeight;
}
if ( GridLines == GridStyles.gridSolid )//offScreenDC.DrawRectangle( Pens.Purple, rect );
DrawGridLines( RowsDC, new Rectangle( 0, 0, sizeRows.Width, sizeRows.Height ), true);
bmpRow.Dispose();
RowsDC.Dispose();
}
public void DrawRow( Bitmap bmpRow, Size sizeRow, int nItem)
{
DW("DrawRow");
Graphics RowDC = Graphics.FromImage( bmpRow );
SolidBrush brushBK;
if ( ( Items[nItem].Selected == true ) && ( FullRowSelect == true ) )
brushBK = new SolidBrush( SelectionColor );
else
brushBK = new SolidBrush( Color.White );
RowDC.FillRectangle( brushBK, 0,0, sizeRow.Width, sizeRow.Height );
brushBK.Dispose();
int nXCursor = 0;
for ( int nSubItem = 0; nSubItem < Columns.Count; /*nSubItem++*/ )
{
if ( ( ( Items[nItem].Selected == true ) && ( FullRowSelect == true ) ) || ( Items[nItem].SubItems[nSubItem].Selected == true ) )
brushBK = new SolidBrush( SelectionColor );
else
brushBK = new SolidBrush( Items[nItem].SubItems[nSubItem].BackColor );
// keep row spanning in mind
int nColumnsSpanned = Items[nItem].SubItems[nSubItem].Span;
int nItemWidth = Columns.GetSpanSize( nSubItem, nColumnsSpanned );
Bitmap bmpSubItem = new Bitmap( nItemWidth, sizeRow.Height );
DrawSubItem( bmpSubItem, new Size( nItemWidth, sizeRow.Height ), nItem, nSubItem, brushBK );
RowDC.DrawImage( bmpSubItem, nXCursor, 0 );
bmpSubItem.Dispose();
nXCursor += nItemWidth; //Columns[nSubItem].Width;
nSubItem += nColumnsSpanned;
brushBK.Dispose();
}
RowDC.Dispose();
}
public void DrawSubItem( Bitmap bmpSubItem, Size sizeSubItem, int nItem, int nColumn, SolidBrush brushBK ) //, ArrayList Item )
{
DW("DrawSubItem");
//DW("DrawSubItem " + Items[nItem].SubItems[nColumn].Text + " " + nItem.ToString() + " " + nColumn.ToString() );
int th, ty, tw, tx;
int nInteriorWidth;
Graphics SubItemDC = Graphics.FromImage( bmpSubItem );
SubItemDC.FillRectangle( brushBK, 0,0, sizeSubItem.Width, sizeSubItem.Height );
if ( Items[nItem].SubItems[nColumn].EmbeddedImage != null )
{
Size sizeImg = Items[nItem].SubItems[nColumn].EmbeddedImage.Size;
th = sizeImg.Height;
MaxHeight = th+(CellPaddingSize*2); // this will only set if autosize is true
if ( th > sizeSubItem.Height-(CellPaddingSize*2) )
th = sizeSubItem.Height-(CellPaddingSize*2);
ty = (sizeSubItem.Height-th)/2;
tw = sizeImg.Width; //(int)SubItemDC.MeasureString( strItemText, Font ).Width;
tx = 0;
nInteriorWidth = sizeSubItem.Width - (CellPaddingSize*2);
switch ( Columns[nColumn].Alignment )
{
case Alignments.alignLeft:
{ // left justified
tx = BorderPadding;
break;
}
case Alignments.alignCenter:
{ // center justified
tx = (Columns[nColumn].Width-tw)/2;
break;
}
case Alignments.alignRight:
{ // right justified
tx = Columns[nColumn].Width - tw - BorderPadding;
break;
}
}
SubItemDC.DrawImage( Items[nItem].SubItems[nColumn].EmbeddedImage, tx, ty );
}
// draw text portion -------------------------------------------------------------------------------------
string strItemText = Items[nItem].SubItems[nColumn].Text;
// calc alignment
//DW( strItemText + " " + SubItemDC.MeasureString( strItemText, Font ).Height.ToString() );
//DW( strItemText + " " + MeasureMultiLineString( strItemText, SubItemDC, Font ).ToString() );
Size measuredSize = MeasureMultiLineString( strItemText, SubItemDC, Font );
th = measuredSize.Height;
MaxHeight = th+(CellPaddingSize*2); // this will only set if autosize is true
if ( th > sizeSubItem.Height-(CellPaddingSize*2) )
th = sizeSubItem.Height-(CellPaddingSize*2);
ty = (sizeSubItem.Height-th)/2;
tw = measuredSize.Width; //(int)SubItemDC.MeasureString( strItemText, Font ).Width;
tx = 0;
nInteriorWidth = sizeSubItem.Width - (CellPaddingSize*2);
switch ( Columns[nColumn].Alignment )
{
case Alignments.alignLeft:
{ // left justified
tx = BorderPadding;
break;
}
case Alignments.alignCenter:
{ // center justified
tx = (Columns[nColumn].Width-tw)/2;
break;
}
case Alignments.alignRight:
{ // right justified
tx = Columns[nColumn].Width - tw - BorderPadding;
break;
}
}
SolidBrush textBrush = new SolidBrush( Items[nItem].SubItems[nColumn].TextColor );
if ( tw > nInteriorWidth )
SubItemDC.DrawString( TruncateString( strItemText, nInteriorWidth, SubItemDC, Font ), Font, textBrush, CellPaddingSize, ty );
else
SubItemDC.DrawString( strItemText, Font, textBrush, tx + CellPaddingSize, ty );
textBrush.Dispose();
SubItemDC.Dispose();
}
public void DrawGridLines( Graphics RowsDC, Rectangle rect, bool bVertLines )
{
DW("DrawGridLines");
int nStartItem = this.vPanelScrollBar.Value;
/* Draw Rows */
int nYCursor = 0;
for (int nItem = 0; ((nItem < (VisibleRows+1) ) && ((nItem+nStartItem) < Items.Count )); nItem++ )
{ //Debug.WriteLine( "ItemCount " + Items.Count.ToString() + " Item Number " + nItem.ToString() );
int nXCursor = 0;
for ( int nColumn = 0; nColumn < Columns.Count; /*nColumn++*/ )
{
int nColumnsSpanned = Items[nItem+nStartItem].SubItems[nColumn].Span;
int nItemWidth = Columns.GetSpanSize( nColumn, nColumnsSpanned );
nXCursor += nItemWidth;
RowsDC.DrawLine( SystemPens.ControlLight, nXCursor, nYCursor, nXCursor, nYCursor+ItemHeight );
nColumn += nColumnsSpanned;
}
nYCursor += ItemHeight;
RowsDC.DrawLine( SystemPens.ControlLight, 0, nYCursor, rect.Width, nYCursor ); // bottom line will always be there
}
}
#endregion // drawing
#region Scrolling
protected void RecalcScroll( )//Graphics g )
{
DW("RecalcScroll");
RedrawState = RedrawStates.RedrawSuspended;
bool bSBChanged;
do // this loop is to handle changes and rechanges that happen when oen or the other changes
{
DW("Begin scrolbar updates loop");
bSBChanged = false;
if ( (Columns.Width > RowsInnerClientRect.Width) && (hPanelScrollBar.Visible == false) )
{ // total width of all the rows is less than the visible rect
hPanelScrollBar.mVisible = true;
hPanelScrollBar.Value = 0;
bSBChanged = true;
ManagedInvalidate();
DW("showing hscrollbar");
}
if ( (Columns.Width <= RowsInnerClientRect.Width) && (hPanelScrollBar.Visible == true) )
{ // total width of all the rows is less than the visible rect
hPanelScrollBar.mVisible = false;
hPanelScrollBar.Value = 0;
bSBChanged = true;
ManagedInvalidate();
DW("hiding hscrollbar");
}
if ( (TotalRowHeight > RowsInnerClientRect.Height) && (vPanelScrollBar.Visible == false) )
{ // total height of all the rows is greater than the visible rect
vPanelScrollBar.mVisible = true;
hPanelScrollBar.Value = 0;
bSBChanged = true;
ManagedInvalidate();
DW("showing vscrollbar");
}
if ( (TotalRowHeight <= RowsInnerClientRect.Height) && (vPanelScrollBar.Visible == true) )
{ // total height of all rows is less than the visible rect
vPanelScrollBar.mVisible = false;
vPanelScrollBar.Value = 0;
bSBChanged = true;
ManagedInvalidate();
DW("hiding vscrollbar");
}
DW("End scrolbar updates loop");
} while ( bSBChanged == true ); // this should never really run more than twice
//Rectangle headerRect = HeaderRect; // tihs is an optimization so header rect doesnt recalc every time we call it
Rectangle rectClient = RowsInnerClientRect;
/*
* now that we know which scrollbars are showing and which aren't, resize the scrollbars to fit those windows
*/
if ( vPanelScrollBar.Visible == true )
{
vPanelScrollBar.mTop = rectClient.Y;
vPanelScrollBar.mLeft = rectClient.Right;
vPanelScrollBar.mHeight = rectClient.Height;
vPanelScrollBar.mLargeChange = VisibleRows;
vPanelScrollBar.mMaximum = Count-1;
if ( ((vPanelScrollBar.Value + VisibleRows) > Count) ) // catch all to make sure the scrollbar isnt going farther than visible items
{
DW("Changing vpanel value");
vPanelScrollBar.Value = Count - VisibleRows; // an item got deleted underneath somehow and scroll value is larger than can be displayed
}
}
if ( hPanelScrollBar.Visible == true )
{
hPanelScrollBar.mLeft = rectClient.Left;
hPanelScrollBar.mTop = rectClient.Bottom;
hPanelScrollBar.mWidth = rectClient.Width;
hPanelScrollBar.mLargeChange = rectClient.Width; // this reall is the size we want to move
hPanelScrollBar.mMaximum = Columns.Width;
if ( (hPanelScrollBar.Value + hPanelScrollBar.LargeChange) > hPanelScrollBar.Maximum )
{
DW("Changing vpanel value");
hPanelScrollBar.Value = hPanelScrollBar.Maximum - hPanelScrollBar.LargeChange;
}
}
//DW("Exit recalc scroll");
RedrawState = RedrawStates.RedrawNormal;
}
private void vPanelScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
{
DW("vPanelScrollBar_Scroll");
ManagedInvalidate();
}
private void hPanelScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
{
DW("hPanelScrollBar_Scroll");
ManagedInvalidate();
}
#endregion
#region Mouse
private void GlacialList_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
DW("GlacialList_MouseDown");
//DW("Button " + e.Button.ToString() );
int nItem = 0, nColumn = 0;
ListStates eState;
InterpretCoords( e.X, e.Y, out nItem, out nColumn, out eState );
if ( e.Button == MouseButtons.Right ) // if its the right button then we don't really care till its released
return;
//-----------------------------------------------------------------------------------------
if ( eState == ListStates.stateColumnSelect ) // Column select
{
m_nState = ListStates.stateNone;
Columns[ nColumn ].State = GLColumn.ColumnStates.csPressed;
if ( ColumnClickedEvent != null )
ColumnClickedEvent( this, new ClickEventArgs( nItem, nColumn ) ); // fire the column clicked event
//ManagedInvalidate();
return;
}
//---Resizing -----------------------------------------------------------------------------------
if ( eState == ListStates.stateColumnResizing ) // resizing
{
Cursor.Current = Cursors.VSplit;
m_nState = ListStates.stateColumnResizing;
m_pointColumnResizeAnchor = new Point( GetColumnScreenX(nColumn), e.Y ); // deal with moving column sizes
m_nResizeColumnNumber = nColumn;
return;
}
//--Item check, if no items exist go no further--
//if ( Items.Count == 0 )
//return;
//---Items --------------------------------------------------------------------------------------
if ( eState == ListStates.stateSelecting )
{ // ctrl based multi select ------------------------------------------------------------
m_nState = ListStates.stateSelecting;
if ( (( ModifierKeys & Keys.Control) == Keys.Control ) && ( AllowMultiselect == true ) )
{
m_nLastSelectionIndex = nItem;
if ( Items[nItem].Selected == true )
Items[nItem].Selected = false;
else
Items[nItem].Selected = true;
return;
}
// shift based multi row select -------------------------------------------------------
if ( (( ModifierKeys & Keys.Shift) == Keys.Shift ) && ( AllowMultiselect == true ) )
{
Items.ClearSelection();
if ( m_nLastSelectionIndex >= 0 ) // ie, non negative so that we have a starting point
{
int index = m_nLastSelectionIndex;
do
{
Items[index].Selected = true;
if ( index > nItem ) index--;
if ( index < nItem ) index++;
} while ( index != nItem );
Items[index].Selected = true;
}
return;
}
// the normal single select -----------------------------------------------------------
Items.ClearSelection();
// following two if statements deal ONLY with non multi=select where a singel sub item is being selected
if ( ( m_nLastSelectionIndex < Count ) && ( m_nLastSubSelectionIndex < Columns.Count ) )
Items[m_nLastSelectionIndex].SubItems[m_nLastSubSelectionIndex].Selected = false;
if ( ( FullRowSelect == false ) && ( ( nItem < Count ) && ( nColumn < Columns.Count ) ) )
Items[nItem].SubItems[nColumn].Selected = true;
m_nLastSelectionIndex = nItem;
m_nLastSubSelectionIndex = nColumn;
Items[nItem].Selected = true;
}
}
private void GlacialList_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
DW("GlacialList_MouseMove");
//Debug.WriteLine( e.X.ToString() + " " + e.Y.ToString() );
try
{
if ( m_nState == ListStates.stateColumnResizing )
{
Cursor.Current = Cursors.VSplit;
int nWidth;
nWidth = e.X - m_pointColumnResizeAnchor.X;
if ( nWidth <= MINIMUM_COLUMN_SIZE )
{
nWidth = MINIMUM_COLUMN_SIZE;
}
GLColumn col;
col = (GLColumn)Columns[m_nResizeColumnNumber];
col.Width = nWidth;
return;
}
int nItem = 0, nSubItem = 0;
ListStates eState;
InterpretCoords( e.X, e.Y, out nItem, out nSubItem, out eState );
if ( eState == ListStates.stateColumnResizing )
{
Cursor.Current = Cursors.VSplit;
return;
}
Cursor.Current = Cursors.Arrow;
}
catch( Exception ex )
{
Debug.WriteLine("Exception throw in GlobalList_MouseMove with text : " + ex.ToString() );
}
}
private void GlacialList_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
{
DW("GlacialList_MouseUp");
Cursor.Current = Cursors.Arrow;
Columns.ClearStates();
int nItem = 0, nColumn = 0;
ListStates eState;
InterpretCoords( e.X, e.Y, out nItem, out nColumn, out eState );
if ( e.Button == MouseButtons.Right )
{
if ( RightClickedEvent != null )
RightClickedEvent( this, new ClickEventArgs( nItem, nColumn ) );
}
else if ( ( e.Button == MouseButtons.Left ) && ( this.m_nState == ListStates.stateSelecting ) )
{
if ( ItemClickedEvent != null )
ItemClickedEvent( this, new ClickEventArgs( nItem, nColumn ) ); // fire the event to all that subscribed
}
m_nState = ListStates.stateNone;
// check to see if there was a column being held down, if so then this should generate the column clicked event
//Invalidate( true );
}
#endregion
#endregion // functionality
}
}