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;
using System.Runtime.InteropServices;
namespace GlacialComponents.Controls
{
public enum GLGridStyles { gridNone=0, gridDashed=1, gridSolid=2 }
public enum GLHeaderStyles
{
Normal = 0,
SuperFlat = 1,
XP = 2
}
/// <summary>
/// Summary description for GlacialList.
/// </summary>
public class GlacialList : System.Windows.Forms.Control
{
#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
}
private void InitializeComponent()
{
}
public static void DI( 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 ColumnClickedEvent;
#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 Definitions
public enum WIN32Codes
{
WM_GETDLGCODE = 0x0087,
WM_SETREDRAW = 0x000B,
WM_CANCELMODE = 0x001F,
WM_NOTIFY = 0x4e,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WM_CHAR = 0x0102,
WM_SYSKEYDOWN = 0x104,
WM_SYSKEYUP = 0x105,
WM_COMMAND = 0x111,
WM_MENUCHAR = 0x120,
WM_MOUSEMOVE = 0x200,
WM_LBUTTONDOWN = 0x201,
WM_MOUSELAST = 0x20a,
WM_USER = 0x0400,
WM_REFLECT = WM_USER + 0x1c00
}
public enum DialogCodes
{
DLGC_WANTARROWS = 0x0001,
DLGC_WANTTAB = 0x0002,
DLGC_WANTALLKEYS = 0x0004,
DLGC_WANTMESSAGE = 0x0004,
DLGC_HASSETSEL = 0x0008,
DLGC_DEFPUSHBUTTON = 0x0010,
DLGC_UNDEFPUSHBUTTON = 0x0020,
DLGC_RADIOBUTTON = 0x0040,
DLGC_WANTCHARS = 0x0080,
DLGC_STATIC = 0x0100,
DLGC_BUTTON = 0x2000,
}
protected const int WM_KEYDOWN = 0x0100;
protected const int VK_LEFT = 0x0025;
protected const int VK_UP = 0x0026;
protected const int VK_RIGHT = 0x0027;
protected const int VK_DOWN = 0x0028;
public enum ListStates { stateNone=0, stateSelecting=1, stateColumnSelect=2, stateColumnResizing=3 }
const int RESIZE_ARROW_PADDING = 2;
const int MINIMUM_COLUMN_SIZE = 4;
#endregion
#region Class Variables
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 ArrayList LiveControls = new ArrayList(); // list of controls currently visible. THIS IS AN OPTIMIZATION. This will keep us from having to iterate the entire list beforehand.
private ArrayList NewLiveControls = new ArrayList();
private System.ComponentModel.Container components = null;
private GlacialComponents.Controls.ManagedVScrollBar vPanelScrollBar;
private GlacialComponents.Controls.ManagedHScrollBar hPanelScrollBar;
private BorderStrip vertLeftBorderStrip;
private BorderStrip vertRightBorderStrip;
private BorderStrip horiBottomBorderStrip;
private BorderStrip horiTopBorderStrip;
private BorderStrip cornerBox;
#endregion
#region ClassProperties
private GLColumnCollection m_Columns;
private GLItemCollection m_Items;
// border
private bool m_bShowBorder = true;
private GLGridStyles m_GridLineStyle = GLGridStyles.gridSolid;
private int m_nItemHeight = 18;
private int m_nHeaderHeight = 22;
private int m_nBorderWidth = 2;
private Color m_colorGridColor = Color.Gray;
private bool m_bMultiSelect = false;
private Color m_colorSelectionColor = Color.DarkBlue;
private bool m_bHeaderVisible = true;
private ImageList m_ImageList = null; // if it doesnt exist, then don't make it yet.
private Color m_SelectedTextColor = Color.White;
private int m_nMaxHeight = 0;
private bool m_bAutoHeight = true;
private bool m_bAllowColumnResize = true;
private bool m_bFullRowSelect = true;
private bool m_bAutoSort = true;
private GLItem m_FocusedItem = null;
private bool m_bShowFocusRect = false;
private bool m_bHotTracking = false;
private int m_nHotColumnIndex = -1; // internal hot column
private int m_nHotItemIndex = -1; // internal hot item index
private Color m_HotTrackingColor = Color.LightGray; // brush color to use
//private bool m_bXPTheme = true; // turn xp themes on for the control
private bool m_bAlternatingColors = false;
private Color m_colorAlternateBackground = Color.DarkGreen;
private Color m_colorSuperFlatHeaderColor = Color.White;
private GLHeaderStyles m_HeaderStyle = GLHeaderStyles.Normal;
private bool m_bItemWordWrap = false;
private bool m_bHeaderWordWrap = false;
#region Control Properties
/// <summary>
/// Word wrap in header
/// </summary>
[
Description("Word wrap in header"),
Category("Header"),
Browsable(true)
]
public bool HeaderWordWrap
{
get { return m_bHeaderWordWrap; }
set { m_bHeaderWordWrap = value; }
}
/// <summary>
/// Word wrap in cells
/// </summary>
[
Description("Word wrap in cells"),
Category("Item"),
Browsable(true)
]
public bool ItemWordWrap
{
get { return m_bItemWordWrap; }
set { m_bItemWordWrap = value; }
}
/// <summary>
/// background color to use if flat
/// </summary>
[
Description("Color for text in boxes that are selected."),
Category("Header"),
Browsable(true)
]
public Color SuperFlatHeaderColor
{
get { return m_colorSuperFlatHeaderColor; }
set { m_colorSuperFlatHeaderColor = value; }
}
/// <summary>
/// Allows for more custom header styles
/// </summary>
[
Description("Allows for more custom header styles"),
Category("Header"),
Browsable(true)
]
public GLHeaderStyles HeaderStyle
{
get { return m_HeaderStyle; }
set
{
m_HeaderStyle = value;
DI("Calling Invalidate from HeaderStyle Property");
Invalidate();
}
}
/// <summary>
/// Alternating Colors on or off
/// </summary>
[
Description("turn xp themes on or not"),
Category("Item Alternating Colors"),
Browsable(true)
]
public bool AlternatingColors
{
get { return m_bAlternatingColors; }
set { m_bAlternatingColors = value; }
}
/// <summary>
/// second background color if we use alternating colors
/// </summary>
[
Description("Color for text in boxes that are selected."),
Category("Item Alternating Colors"),
Browsable(true)
]
public Color AlternateBackground
{
get { return m_colorAlternateBackground; }
set { m_colorAlternateBackground = value; }
}
/// <summary>
/// Whether or not to show a border.
/// </summary>
[
Description("Whether or not to show a border."),
Category("Appearance"),
Browsable(true),
]
public bool ShowBorder
{
get { return m_bShowBorder; }
set { m_bShowBorder = value; }
}
/// <summary>
/// Color for text in boxes that are selected
/// </summary>
[
Description("Color for text in boxes that are selected."),
Category("Item"),
Browsable(true)
]
public Color SelectedTextColor
{
get { return m_SelectedTextColor; }
set { m_SelectedTextColor = value; }
}
/// <summary>
/// hot tracking
/// </summary>
[
Description("Color for hot tracking."),
Category("Appearance"),
Browsable(true)
]
public Color HotTrackingColor
{
get { return m_HotTrackingColor; }
set { m_HotTrackingColor = value; }
}
/// <summary>
/// Hot Tracking of columns and items
/// </summary>
[
Description("Show hot tracking."),
Category("Behavior"),
Browsable(true)
]
public bool HotTracking
{
get { return m_bHotTracking; }
set { m_bHotTracking = value; }
}
/// <summary>
/// Show the focus rect or not
/// </summary>
[
Description("Show Focus Rect on items."),
Category("Item"),
Browsable(true)
]
public bool ShowFocusRect
{
get { return m_bShowFocusRect; }
set { m_bShowFocusRect = value; }
}
/// <summary>
/// auto sorting
/// </summary>
[
Description("Autosort items as they are added."),
Category("Item"),
Browsable(true),
]
public bool AutoSort
{
get { return m_bAutoSort; }
set { m_bAutoSort = value; }
}
/// <summary>
///
/// </summary>
[
Description("ImageList to be used in listview."),
Category("Behavior"),
Browsable(true),
]
public ImageList ImageList
{
get { return m_ImageList; }
set { m_ImageList = value; }
}
/// <summary>
/// Allow columns to be resized
/// </summary>
[
Description("Allow resizing of columns"),
Category("Header"),
Browsable(true)
]
public bool AllowColumnResize
{
get { return m_bAllowColumnResize; }
set { m_bAllowColumnResize = value; }
}
/// <summary>
/// Control resizes height of row based on size.
/// </summary>
[
Description("Do we want rows to automatically adjust height"),
Category("Item"),
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("Header"),
Browsable(true)
]
public bool HeaderVisible
{
get { return m_bHeaderVisible; }
set { m_bHeaderVisible = value; }
}
/// <summary>
/// Collection of columns
/// </summary>
[
Category("Header"),
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; }
}
/// <summary>
/// selection bar color
/// </summary>
[
Description("Background color to mark selection."),
Category("Item"),
Browsable(true),
]
public Color SelectionColor
{
get { return m_colorSelectionColor; }
set { m_colorSelectionColor = value; }
}
[
Description("Allow full row select."),
Category("Item"),
Browsable(true)
]
public bool FullRowSelect
{
get { return m_bFullRowSelect; }
set { m_bFullRowSelect = value; }
}
[
Description("Allow multiple selections."),
Category("Item"),
Browsable(true)
]
public bool AllowMultiselect
{
get { return m_bMultiSelect; }
set { m_bMultiSelect = value; }
}
[
Description("Border Padding"),
Category("Appearance"),
Browsable(false),
DefaultValue(2)
]
public int BorderPadding
{
get
{
if ( ShowBorder )
return 2;
else
return 0;
}
set { m_nBorderWidth = value; }
}
[
Description("Whether or not to draw gridlines"),
Category("Grid"),
Browsable(true)
]
public GLGridStyles GLGridLines
{
get { return m_GridLineStyle; }
set
{
m_GridLineStyle = value;
DI("Calling Invalidate From GLGridStyles");
Invalidate();
}
}
[
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;
DI("Calling Invalidate From GridColor");
Invalidate();
}
}
/// <summary>
/// how big do we want the individual items to be
/// </summary>
[
Description("How high each row is."),
Category("Item"),
Browsable(true)
]
public int ItemHeight
{
get { return m_nItemHeight; }
set
{
//Debug.WriteLine( "Setting item height to " + value.ToString() );
//if ( value == 15 )
//Debug.WriteLine( "stop" );
m_nItemHeight = value;
DI("Calling Invalidate From ItemHeight");
Invalidate();
}
}
[
Description("How high the columns are."),
Category("Header"),
Browsable(true)
]
public int HeaderHeight
{
get
{
if ( HeaderVisible == true )
return m_nHeaderHeight;
else
return 0;
}
set
{
m_nHeaderHeight = value;
DI("Calling Invalidate From HeaderHeight");
Invalidate();
}
}
/// <summary>
/// amount of space inside any given cell to borders
/// </summary>
[
Description("Cell padding area"),
Browsable(false)
]
public int CellPaddingSize
{
get { return 2; } // default I set to 4
}
#endregion
#region Working Properties
private int m_nSortIndex = 0;
/// <summary>
/// Current index we are sorting against
/// </summary>
[
Description("Current index we are sorting against"),
Browsable(false)
]
public int SortIndex
{
get
{
return m_nSortIndex;
}
set
{
m_nSortIndex = value;
}
}
/// <summary>
/// currently focused item
/// </summary>
[
Description("Currently Focused Column"),
Browsable(false)
]
public int HotColumnIndex
{
get
{
return m_nHotColumnIndex;
}
set
{
if ( m_bHotTracking )
if ( m_nHotColumnIndex != value )
{
m_nHotItemIndex = -1;
m_nHotColumnIndex = value;
DI("Calling Invalidate From HotColumnIndex");
Invalidate();
}
}
}
/// <summary>
/// currently focused item
/// </summary>
[
Description("Currently Focused Item"),
Browsable(false)
]
public int HotItemIndex
{
get
{
return m_nHotItemIndex;
}
set
{
if ( m_bHotTracking )
if ( m_nHotItemIndex != value )
{
m_nHotColumnIndex = -1;
m_nHotItemIndex = value;
DI("Calling Invalidate From HotItemIndex");
Invalidate();
}
}
}
/// <summary>
/// currently focused item
/// </summary>
[
Description("Currently Focused Item"),
Browsable(false)
]
public GLItem FocusedItem
{
get
{
if ( !m_bShowFocusRect ) // if they elect not to see the focus rect, then dont show it
return null;
return m_FocusedItem;
}
set
{
if ( m_FocusedItem != value )
{
m_FocusedItem = value;
DI("Calling Invalidate From FocusedItem");
Invalidate();
}
}
}
[
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 VisibleRowsCount
{
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;
DI("Calling Invalidate From MaxHeight");
Invalidate();
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);
#if false // it really shouldnt be the business of this routine to deal with only a resize problem
if ( (tmpHeight % ItemHeight) != 0 )
{ // the size must be adjusted for the control, this is not the right size
int nRows = tmpHeight / ItemHeight;
int controlHeight = (nRows * ItemHeight) + tmpY + (BorderPadding*2);
this.SetBounds( 0, 0, Width, controlHeight );
Invalidate(true);
return RowsClientRect;
}
#endif
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 RowsRect
{
get
{
Rectangle rect = new Rectangle();
rect.X = -this.hPanelScrollBar.Value;
rect.Y = HeaderHeight + BorderPadding;
rect.Width = Columns.Width;
rect.Height = this.VisibleRowsCount * ItemHeight;
return rect;
}
}
[
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");
components = new System.ComponentModel.Container();
this.TabStop = true;
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 );
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.Opaque |
ControlStyles.UserPaint |
ControlStyles.DoubleBuffer |
ControlStyles.Selectable |
ControlStyles.UserMouse,
true
);
this.BackColor = SystemColors.ControlLightLight;
this.hPanelScrollBar = new GlacialComponents.Controls.ManagedHScrollBar();
this.vPanelScrollBar = new GlacialComponents.Controls.ManagedVScrollBar();
//
// Creating borders
//
//Debug.WriteLine( "Creating borders" );
this.vertLeftBorderStrip = new BorderStrip();
this.vertRightBorderStrip = new BorderStrip();
this.horiBottomBorderStrip = new BorderStrip();
this.horiTopBorderStrip = new BorderStrip();
this.cornerBox = new BorderStrip();
this.SuspendLayout();
//
// hPanelScrollBar
//
this.hPanelScrollBar.Anchor = System.Windows.Forms.AnchorStyles.None;
this.hPanelScrollBar.CausesValidation = false;
this.hPanelScrollBar.Location = new System.Drawing.Point(24, 0);
this.hPanelScrollBar.mHeight = 16;
this.hPanelScrollBar.mWidth = 120;
this.hPanelScrollBar.Name = "hPanelScrollBar";
this.hPanelScrollBar.Size = new System.Drawing.Size(120, 16);
this.hPanelScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.hPanelScrollBar_Scroll);
this.hPanelScrollBar.Parent = this;
this.Controls.Add( hPanelScrollBar );
//
// vPanelScrollBar
//
this.vPanelScrollBar.Anchor = System.Windows.Forms.AnchorStyles.None;
this.vPanelScrollBar.CausesValidation = false;
this.vPanelScrollBar.Location = new System.Drawing.Point(0, 12);
this.vPanelScrollBar.mHeight = 120;
this.vPanelScrollBar.mWidth = 16;
this.vPanelScrollBar.Name = "vPanelScrollBar";
this.vPanelScrollBar.Size = new System.Drawing.Size(16, 120);
this.vPanelScrollBar.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vPanelScrollBar_Scroll);
this.vPanelScrollBar.Parent = this;
this.Controls.Add( vPanelScrollBar );
this.horiTopBorderStrip.Parent = this;
this.horiTopBorderStrip.BorderType = BorderStrip.BorderTypes.btTop;
this.horiTopBorderStrip.Visible = true;
this.horiTopBorderStrip.BringToFront();
//this.horiBottomBorderStrip.BackColor=Color.Black;
this.horiBottomBorderStrip.Parent = this;
this.horiBottomBorderStrip.BorderType = BorderStrip.BorderTypes.btBottom;
this.horiBottomBorderStrip.Visible = true;
this.horiBottomBorderStrip.BringToFront();
//this.vertLeftBorderStrip.BackColor=Color.Black;
this.vertLeftBorderStrip.BorderType = BorderStrip.BorderTypes.btLeft;
this.vertLeftBorderStrip.Parent = this;
this.vertLeftBorderStrip.Visible = true;
this.vertLeftBorderStrip.BringToFront();
//this.vertRightBorderStrip.BackColor=Color.Black;
this.vertRightBorderStrip.BorderType = BorderStrip.BorderTypes.btRight;
this.vertRightBorderStrip.Parent = this;
this.vertRightBorderStrip.Visible = true;
this.vertRightBorderStrip.BringToFront();
this.cornerBox.BackColor = SystemColors.Control;
this.cornerBox.BorderType = BorderStrip.BorderTypes.btSquare;
this.cornerBox.Visible = false;
this.cornerBox.Parent = this;
this.cornerBox.BringToFront();
this.Name = "GlacialList";
this.ResumeLayout(false);
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
Debug.WriteLine( "Disposing Glacial List." );
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#endregion
#region System Overrides
/// <summary>
/// stop keys from going to scrollbars
/// </summary>
/// <param name="m"></param>
protected override void WndProc(ref Message msg)
{
base.WndProc(ref msg);
if (msg.Msg == (int)WIN32Codes.WM_GETDLGCODE)
{
msg.Result = new IntPtr((int)DialogCodes.DLGC_WANTCHARS | (int)DialogCodes.DLGC_WANTARROWS | msg.Result.ToInt32());
}
}
/// <summary>
/// keep certain keys here
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
public override bool PreProcessMessage(ref Message msg)
{
if (msg.Msg == WM_KEYDOWN)
{
Keys keyData = ((Keys) (int) msg.WParam) | ModifierKeys;
Keys keyCode = ((Keys) (int) msg.WParam);
if ( keyData == Keys.Escape )
{
FocusedItem = null;
}
else if ( ( FocusedItem != null ) && ( Count > 0 ) )
{
int nItemIndex = Items.FindItemIndex( FocusedItem );
if ( nItemIndex < 0 )
return true; // this can't move
if (keyData == Keys.Down)
{
nItemIndex++;
}
else if (keyData == Keys.Up)
{
nItemIndex--;
}
else if ( keyData == Keys.PageDown )
{
nItemIndex+=this.VisibleRowsCount;
}
else if ( keyData == Keys.PageUp )
{
nItemIndex-=this.VisibleRowsCount;
}
// bounds check them
if ( nItemIndex < 0 )
nItemIndex = 0;
if ( nItemIndex > Count-1 )
nItemIndex = Count-1;
// move view
if (nItemIndex < this.vPanelScrollBar.Value) // its out of viewable, move the surface
this.vPanelScrollBar.Value = nItemIndex;
if ( nItemIndex > ( this.vPanelScrollBar.Value+this.VisibleRowsCount ))
this.vPanelScrollBar.Value = nItemIndex - this.VisibleRowsCount;
FocusedItem = Items[nItemIndex];
}
else
{ // else we just move the actual list
if ( this.vPanelScrollBar.Visible )
{ // only bother if we at least have a scrollbar
int nCVP = this.vPanelScrollBar.Value;
if (keyData == Keys.Down)
{
if ( ( nCVP + this.VisibleRowsCount ) < Count )
this.vPanelScrollBar.Value+=1;
}
else if (keyData == Keys.Up)
{
if ( nCVP > 0 )
this.vPanelScrollBar.Value-=1;
}
else if ( keyData == Keys.PageDown )
{
if ( ( nCVP + this.VisibleRowsCount + this.VisibleRowsCount ) < Count )
this.vPanelScrollBar.Value = nCVP + this.VisibleRowsCount;
else
this.vPanelScrollBar.Value = Count - this.VisibleRowsCount;
}
else if ( keyData == Keys.PageUp )
{
if ( ( nCVP - this.VisibleRowsCount ) > -1 )
this.vPanelScrollBar.Value = nCVP - this.VisibleRowsCount;
else
this.vPanelScrollBar.Value = 0;
}
if ( nCVP != this.vPanelScrollBar.Value )
Invalidate();
}
}
}
return base.PreProcessMessage(ref msg);
}
#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
DI("Calling Invalidate From Items_Changed");
Invalidate();
}
public void Columns_Changed( object source, ChangedEventArgs e )
{
DW("Columns_Changed");
if ( ColumnChangedEvent != null )
ColumnChangedEvent( this, e ); // fire the column clicked event
DI("Calling Invalidate From Columns_Changed");
Invalidate();
}
#endregion
#region HelperFunctions
/// <summary>
/// interpret mouse coordinates
/// </summary>
/// <param name="nScreenX"></param>
/// <param name="nScreenY"></param>
/// <param name="nItem"></param>
/// <param name="nColumn"></param>
/// <param name="nState"></param>
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
foreach ( GLColumn col in Columns )
{
if ( (nScreenX > nCurrentX) && (nScreenX < (nCurrentX+col.Width-RESIZE_ARROW_PADDING)) )
{
nState = ListStates.stateColumnSelect;
break;
}
if ( (nScreenX > (nCurrentX+col.Width-RESIZE_ARROW_PADDING)) && (nScreenX < (nCurrentX+col.Width+RESIZE_ARROW_PADDING)) )
{
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
Columns.ClearHotStates();
nItem = ((nScreenY - RowsInnerClientRect.Y) / ItemHeight) + vPanelScrollBar.Value;
this.HotItemIndex = nItem;
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;
}
else
{
if ( ( nScreenY >= this.HeaderRect.Y ) && ( nScreenY < this.HeaderRect.Bottom ) )
{
this.HotColumnIndex = nColumn;
if ( ( ( nColumn > -1 ) && ( nColumn < Columns.Count ) ) && (!Columns.AnyPressed() ) )
if ( Columns[nColumn].State == ColumnStates.csNone)
{
Columns.ClearHotStates();
Columns[nColumn].State = ColumnStates.csHot;
}
}
}
return;
}
/// <summary>
/// return the X starting point of a particular column
/// </summary>
/// <param name="nColumn"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Sort a column.
///
/// Set to virtual so you can write your own sorting
/// </summary>
/// <param name="nColumn"></param>
public virtual void SortColumn( int nColumn )
{
Debug.WriteLine( "Column sorting called." );
if ( Count < 2 ) // nothing to sort
return;
this.SortIndex = nColumn;
Items.Sort();
}
#endregion
#region Dimensions
protected override void OnResize(EventArgs e)
{
DW("GlacialList_Resize");
//RecalcScroll();
DI("Calling Invalidate From OnResize");
Invalidate();
}
#endregion
#region Drawing
/// <summary>
/// Paint
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
///
protected override void OnPaint(PaintEventArgs e)
{
DW("Paint");
RecalcScroll();
Debug.WriteLine( "Redraw called " + DateTime.Now.ToLongTimeString() );
Graphics g = e.Graphics;
if ( Columns.Count > 0 )
{
int nInsideWidth;
if ( Columns.Width > HeaderRect.Width )
nInsideWidth = Columns.Width;
else
nInsideWidth = HeaderRect.Width;
/*
* draw header
*/
if ( HeaderVisible == true )
{
g.SetClip( HeaderRect );
DrawHeader( g, new Size( HeaderRect.Width, HeaderRect.Height ) );
}
/*
* draw client area
*/
g.SetClip( RowsInnerClientRect );
DrawRows( g );
// very optimized way of removing controls that aren't visible anymore without having to iterate the entire items list
foreach( Control control in LiveControls )
{
Debug.WriteLine( "Setting " + control.ToString() + " to hidden." );
control.Visible = false; // make sure the controls that aren't visible aren't shown
}
LiveControls = NewLiveControls;
NewLiveControls = new ArrayList();
}
g.SetClip( this.ClientRectangle );
}
/// <summary>
/// Draw Header Control
/// </summary>
/// <param name="graphicHeader"></param>
/// <param name="sizeHeader"></param>
public void DrawHeader( Graphics graphicHeader, /*Bitmap bmpHeader,*/ Size sizeHeader )
{
DW("DrawHeader");
if ( this.HeaderStyle == GLHeaderStyles.SuperFlat )
{
SolidBrush brush = new SolidBrush( this.SuperFlatHeaderColor );
graphicHeader.FillRectangle( brush, HeaderRect );
brush.Dispose();
}
else
{
graphicHeader.FillRectangle( SystemBrushes.Control, HeaderRect );
}
if ( Columns.Count <= 0 )
return;
// draw vertical lines first, then horizontal lines
int nCurrentX = (-this.hPanelScrollBar.Value) + HeaderRect.X;
foreach ( GLColumn column in Columns )
{
// cull columns that won't be drawn first
if ( ( nCurrentX + column.Width ) < 0 )
{
nCurrentX += column.Width;
continue; // skip this column, its not being drawn
}
if ( nCurrentX > HeaderRect.Right )
return; // were past the end of the visible column, stop drawing
DrawColumnHeader( graphicHeader, new Rectangle( nCurrentX, HeaderRect.Y, column.Width, HeaderHeight ), column );
nCurrentX += column.Width; // move the parser
}
}
/// <summary>
/// Draw column in header control
/// </summary>
/// <param name="graphicsColumn"></param>
/// <param name="rectColumn"></param>
/// <param name="column"></param>
public void DrawColumnHeader( Graphics graphicsColumn, Rectangle rectColumn, GLColumn column )
{
DW("DrawColumn");
if ( this.HeaderStyle == GLHeaderStyles.SuperFlat )
{
SolidBrush brush = new SolidBrush( this.SuperFlatHeaderColor );
graphicsColumn.FillRectangle( brush, rectColumn );
brush.Dispose();
}
else if (( this.HeaderStyle == GLHeaderStyles.XP )&& OPaC.uxTheme.Wrapper.IsAppThemed() )
{ // this is really the only thing we care about for themeing right now inside the control
System.IntPtr hDC = graphicsColumn.GetHdc();;
//Debug.WriteLine("hci is " + this.HotColumnIndex.ToString() );
Debug.WriteLine("CH rect " + rectColumn.ToString() );
if ( column.State == ColumnStates.csNone )
{
Debug.WriteLine( "Normal" );
OPaC.uxTheme.Wrapper.DrawBackground( "HEADER", "HEADERITEM", "NORMAL", hDC, rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom,
rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom );
}
else if ( column.State == ColumnStates.csPressed )
{
Debug.WriteLine( "Pressed" );
OPaC.uxTheme.Wrapper.DrawBackground( "HEADER", "HEADERITEM", "PRESSED", hDC, rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom,
rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom );
}
else if ( column.State == ColumnStates.csHot )
{
Debug.WriteLine( "Hot" );
OPaC.uxTheme.Wrapper.DrawBackground( "HEADER", "HEADERITEM", "HOT", hDC, rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom,
rectColumn.X, rectColumn.Y, rectColumn.Right, rectColumn.Bottom );
}
graphicsColumn.ReleaseHdc(hDC);
}
else
{
if ( column.State != ColumnStates.csPressed )
ControlPaint.DrawButton( graphicsColumn, rectColumn, ButtonState.Normal );
else
ControlPaint.DrawButton( graphicsColumn, rectColumn, ButtonState.Pushed );
}
// if there is an image, this routine will RETURN with exactly the space left for everything else after the image is drawn (or not drawn due to lack of space)
if ( (column.ImageIndex > -1) && (ImageList != null) && (column.ImageIndex < this.ImageList.Images.Count) )
rectColumn = DrawCellGraphic( graphicsColumn, rectColumn, this.ImageList.Images[ column.ImageIndex ], HorizontalAlignment.Left );
DrawCellText( graphicsColumn, rectColumn, column.Text, column.TextAlignment, this.ForeColor, false, HeaderWordWrap );
// dont really need the arrow
// if ( ( ta > -25 ) && ( column.LastSortState == GLColumn.ColumnSortState.SortedDown ) )
// {
// int tay = rectColumn.Y + (( rectColumn.Height - 6 )/2);
// graphicsColumn.DrawLine( SystemPens.ControlDarkDark, ta, tay, ta + 6, tay + 6 );
// graphicsColumn.DrawLine( SystemPens.ControlDarkDark, ta + 12, tay, ta + 6, tay + 6 );
// graphicsColumn.DrawLine( SystemPens.ControlDarkDark, ta, tay, ta + 12, tay );
// }
}
/// <summary>
/// Draw client rows of list control
/// </summary>
/// <param name="bmpRows"></param>
/// <param name="sizeRows"></param>
public void DrawRows( Graphics graphicsRows )
{
DW("DrawRows");
SolidBrush brush = new SolidBrush( this.BackColor );
graphicsRows.FillRectangle( brush, this.RowsClientRect );
brush.Dispose();
// 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;
Rectangle rectRow = this.RowsRect;
rectRow.Height = ItemHeight;
/* Draw Rows */
for ( int nItem = 0; ((nItem < (VisibleRowsCount +1) ) && ((nItem+nStartItem) < Items.Count )); nItem++ )
{
DrawRow( graphicsRows, rectRow, this.Items[ nItem+nStartItem ], nItem+nStartItem );
rectRow.Y += ItemHeight;
}
if ( GLGridLines == GLGridStyles.gridSolid )
DrawGridLines( graphicsRows, this.RowsInnerClientRect );
// draw hot tracking column
if ( ( this.HotColumnIndex != -1 ) && ( HotColumnIndex < Columns.Count ) )
{
int nXCursor = -this.hPanelScrollBar.Value;
for ( int nColumnIndex = 0; nColumnIndex < this.HotColumnIndex; nColumnIndex++ )
nXCursor += Columns[nColumnIndex].Width;
Color transparentColor = Color.FromArgb( 100, 182, 189, 210 );
Brush hotBrush = new SolidBrush(transparentColor);
graphicsRows.FillRectangle( hotBrush, nXCursor, RowsInnerClientRect.Y, Columns[HotColumnIndex].Width+1, RowsInnerClientRect.Height-1 );
hotBrush.Dispose();
}
}
/// <summary>
/// Draw Row to screen
/// </summary>
/// <param name="graphicsRow"></param>
/// <param name="rectRow"></param>
/// <param name="item"></param>
public void DrawRow( Graphics graphicsRow, Rectangle rectRow, GLItem item, int nItemIndex )
{
DW("DrawRow");
// row background, if its selected, that trumps all, if not then see if we are using alternating colors, if not draw normal
// note, this can all be overridden by the sub item background property
if ( item.Selected )
{
SolidBrush brushBK;
brushBK = new SolidBrush( Color.FromArgb( 255, SelectionColor.R, SelectionColor.G, SelectionColor.B ) );
// *** WARNING *** need to check for full row select here
if ( !FullRowSelect )
{ // calculate how far into the control it goes
int nWidthFR = -this.hPanelScrollBar.Value + Columns.Width;
graphicsRow.FillRectangle( brushBK, this.RowsInnerClientRect.X, rectRow.Y, nWidthFR, rectRow.Height );
}
else
graphicsRow.FillRectangle( brushBK, this.RowsInnerClientRect.X, rectRow.Y, this.RowsInnerClientRect.Width, rectRow.Height );
//graphicsRow.FillRectangle( brushBK, rectRow );
brushBK.Dispose();
}
else
{
SolidBrush brushBK = null;
if ( this.AlternatingColors )
{
int nACItemIndex = Items.FindItemIndex( item );
if ( ( nACItemIndex % 2 ) > 0 )
brushBK = new SolidBrush( this.AlternateBackground );
else
brushBK = new SolidBrush( this.BackColor );
}
else
brushBK = new SolidBrush( this.BackColor );
graphicsRow.FillRectangle( brushBK, this.RowsInnerClientRect.X, rectRow.Y, this.RowsInnerClientRect.Width, rectRow.Height );
brushBK.Dispose();
}
// draw the row of sub items
int nXCursor = -this.hPanelScrollBar.Value;
for ( int nSubItem = 0; nSubItem < Columns.Count; nSubItem++ )
{
Rectangle rectSubItem = new Rectangle( nXCursor, rectRow.Y, Columns[nSubItem].Width, rectRow.Height );
// avoid drawing items that are not in the visible region
if ( ( rectSubItem.Right < 0 ) || ( rectSubItem.Left > this.RowsInnerClientRect.Right ) )
Debug.Write( "" );
else
DrawSubItem( graphicsRow, rectSubItem, item, item.SubItems[nSubItem], nSubItem );
nXCursor += Columns[nSubItem].Width;
}
if ( nItemIndex == this.HotItemIndex ) // handle hot tracking of items
{
Color transparentColor = Color.FromArgb( 75, 182, 189, 210 );
Brush hotBrush = new SolidBrush(transparentColor);
graphicsRow.FillRectangle( hotBrush, this.RowsInnerClientRect.X, rectRow.Y, this.RowsInnerClientRect.Width, rectRow.Height );
hotBrush.Dispose();
}
if ( FocusedItem == item ) // deal with focus rect
{
//ControlPaint.DrawFocusRectangle( graphicsRow, new Rectangle( rectRow.X+1, rectRow.Y+1, rectRow.Width-1, rectRow.Height-1 ) );
ControlPaint.DrawFocusRectangle( graphicsRow, new Rectangle( this.RowsInnerClientRect.X+1, rectRow.Y+1, this.RowsInnerClientRect.Width-1, rectRow.Height-1 ) );
}
}
/// <summary>
/// draw sub item
/// </summary>
/// <param name="graphicsSubItem"></param>
/// <param name="rectSubItem"></param>
/// <param name="subItem"></param>
/// <param name="nColumn"></param>
public void DrawSubItem( Graphics graphicsSubItem, Rectangle rectSubItem, GLItem item, GLSubItem subItem, int nColumn )
{
DW("DrawSubItem");
// precheck to make sure this is big enough for the things we want to do inside it
Rectangle subControlRect = new Rectangle( rectSubItem.X, rectSubItem.Y, rectSubItem.Width, rectSubItem.Height );
if ( ( subItem.Control != null ) && (!subItem.ForceText ) )
{ // custom embedded control here
Control control = subItem.Control;
Rectangle subrc = new Rectangle(
subControlRect.X+this.CellPaddingSize,
subControlRect.Y+this.CellPaddingSize,
subControlRect.Width-this.CellPaddingSize*2,
subControlRect.Height-this.CellPaddingSize*2 );
Type tp = control.GetType();
PropertyInfo pi = control.GetType().GetProperty( "PreferredHeight" );
if ( pi != null )
{
int PreferredHeight = (int)pi.GetValue( control, null );
if ( ( (PreferredHeight + this.CellPaddingSize*2)> this.ItemHeight ) && AutoHeight )
this.ItemHeight = PreferredHeight + this.CellPaddingSize*2;
subrc.Y = subControlRect.Y + ((subControlRect.Height - PreferredHeight)/2);
}
NewLiveControls.Add( control ); // put it in the new list, remove from old list
if ( LiveControls.Contains( control ) ) // make sure its in the old list first
{
LiveControls.Remove( control ); // remove it from list so it doesn't get put down
}
if ( control.Bounds.ToString() != subrc.ToString() )
control.Bounds = subrc; // this will force an invalidation
if ( control.Visible != true )
control.Visible = true;
}
else // not control based
{
// if there is an image, this routine will RETURN with exactly the space left for everything else after the image is drawn (or not drawn due to lack of space)
if ( (subItem.ImageIndex > -1) && (ImageList != null) && (subItem.ImageIndex < this.ImageList.Images.Count) )
rectSubItem = DrawCellGraphic( graphicsSubItem, rectSubItem, this.ImageList.Images[ subItem.ImageIndex ], subItem.ImageAlignment );
// deal with text color in a box on whether it is selected or not
Color textColor;
if ( item.Selected )
textColor = this.SelectedTextColor;
else
textColor = item.TextColor;
DrawCellText( graphicsSubItem, rectSubItem, subItem.Text, Columns[nColumn].TextAlignment, textColor, item.Selected, ItemWordWrap );
}
}
/// <summary>
/// draw the contents of a cell, do not draw any background or associated things
/// </summary>
/// <param name="graphicsCell"></param>
/// <param name="rectCell"></param>
/// <param name="img"></param>
/// <param name="alignment"></param>
/// <returns>
/// returns the area of the cell that is left for you to put anything else on.
/// </returns>
public Rectangle DrawCellGraphic( Graphics graphicsCell, Rectangle rectCell, Image img, HorizontalAlignment alignment )
{
int th, ty, tw, tx;
th = img.Height + (CellPaddingSize*2);
tw = img.Width + (CellPaddingSize*2);
MaxHeight = th; // this will only set if autosize is true
if ( ( tw > rectCell.Width ) || ( th > rectCell.Height ) )
return rectCell; // not enough room to draw the image, bail out
if ( alignment == HorizontalAlignment.Left )
{
ty = rectCell.Y + CellPaddingSize + ((rectCell.Height-th)/2);
tx = rectCell.X + CellPaddingSize;
graphicsCell.DrawImage( img, tx, ty );
// remove the width that we used for the graphic from the cell
rectCell.Width -= (img.Width + (CellPaddingSize*2));
rectCell.X += tw;
}
else if ( alignment == HorizontalAlignment.Center )
{
ty = rectCell.Y + CellPaddingSize + ((rectCell.Height-th)/2);
tx = rectCell.X + CellPaddingSize + ((rectCell.Width-tw)/2);;
graphicsCell.DrawImage( img, tx, ty );
// remove the width that we used for the graphic from the cell
//rectCell.Width -= (img.Width + (CellPaddingSize*2));
//rectCell.X += (img.Width + (CellPaddingSize*2));
rectCell.Width = 0;
}
else if ( alignment == HorizontalAlignment.Right )
{
ty = rectCell.Y + CellPaddingSize + ((rectCell.Height-th)/2);
tx = rectCell.Right - tw;
graphicsCell.DrawImage( img, tx, ty );
// remove the width that we used for the graphic from the cell
rectCell.Width -= tw;
}
return rectCell;
}
/// <summary>
/// Draw cell text is used by header and cell to draw properly aligned text in subitems.
/// </summary>
/// <param name="graphicsCell"></param>
/// <param name="rectCell"></param>
/// <param name="strCellText"></param>
/// <param name="alignment"></param>
public void DrawCellText( Graphics graphicsCell, Rectangle rectCell, string strCellText, ContentAlignment alignment, Color textColor, bool bSelected, bool bWordWrap )
{
int nInteriorWidth = rectCell.Width - (CellPaddingSize*2);
int nInteriorHeight = rectCell.Height - (CellPaddingSize*2);
// deal with text color in a box on whether it is selected or not
SolidBrush textBrush;
if ( bSelected )
textBrush = new SolidBrush( this.SelectedTextColor );
else
textBrush = new SolidBrush( textColor );
// convert property editor friendly alignment to an alignment we can use for strings
StringFormat sf = new StringFormat();
sf.Alignment = GLStringHelpers.ConvertContentAlignmentToHorizontalStringAlignment( alignment );
sf.LineAlignment = GLStringHelpers.ConvertContentAlignmentToVerticalStringAlignment( alignment );
SizeF measuredSize;
if ( bWordWrap )
{
sf.FormatFlags = 0; // word wrapping is on by default for drawing
measuredSize = graphicsCell.MeasureString( strCellText, Font, new Point( BorderPadding, BorderPadding ), sf );
}
else
{ // they aren't word wrapping so we need to put the ...'s where necessary
sf.FormatFlags = StringFormatFlags.NoWrap;
measuredSize = graphicsCell.MeasureString( strCellText, Font, new Point( BorderPadding, BorderPadding ), sf );
if ( measuredSize.Width > nInteriorWidth ) // dont truncate if we are doing word wrap
strCellText = GLStringHelpers.TruncateString( strCellText, nInteriorWidth, graphicsCell, Font );
}
MaxHeight = (int)measuredSize.Height + (BorderPadding*2); // this will only set if autosize is true
graphicsCell.DrawString( strCellText, Font, textBrush, rectCell /*rectCell.X+this.BorderPadding, rectCell.Y+this.BorderPadding*/, sf );
textBrush.Dispose();
}
/// <summary>
/// draw grid lines in row area
/// </summary>
/// <param name="RowsDC"></param>
/// <param name="rect"></param>
/// <param name="bVertLines"></param>
public void DrawGridLines( Graphics RowsDC, Rectangle rect )
{
DW("DrawGridLines");
int nStartItem = this.vPanelScrollBar.Value;
/* Draw Rows */
int nYCursor = rect.Y;
//for (int nItem = 0; ((nItem < (VisibleRowsCount +1) ) && ((nItem+nStartItem) < Items.Count )); nItem++ )
for (int nItem = 0; (nItem < (VisibleRowsCount +1)); nItem++ )
{ //Debug.WriteLine( "ItemCount " + Items.Count.ToString() + " Item Number " + nItem.ToString() );
nYCursor += ItemHeight;
// draw horizontal line
RowsDC.DrawLine( SystemPens.ControlLight, 0, nYCursor, rect.Width, nYCursor ); // bottom line will always be there
}
int nXCursor = -this.hPanelScrollBar.Value;
for ( int nColumn = 0; nColumn < Columns.Count; nColumn++ )
{
// draw vertical line
nXCursor += Columns[nColumn].Width;
RowsDC.DrawLine( SystemPens.ControlLight, nXCursor, rect.Y, nXCursor, rect.Bottom );
}
}
#endregion // drawing
#region Keyboard
protected override void OnKeyPress(KeyPressEventArgs e)
{
e.Handled = true;
}
protected override void OnKeyDown(KeyEventArgs e)
{
e.Handled = true;
}
#endregion
#region Scrolling
// Handlers for scrollbars scroll
protected void OnScroll(object sender, EventArgs e)
{
//GenerateColumnRects();
DI("Calling Invalidate From OnScroll");
Invalidate();
}
#if false
protected void RecalcSubControls()
{
for ( int index = 0, nVisibleItem = 0; index < this.Count; index++ )
{
// test and see if this is visible or not
if ( ( index < this.vPanelScrollBar.Value ) || ( index > ( this.vPanelScrollBar.Value + ( this.VisibleRowsCount ) ) ) )
{
for ( int subindex = 0; subindex < Columns.Count; subindex++ )
{
if ( ( Columns[ subindex ].ControlType != GLColumn.ColumnControlTypes.None ) && ( Items[index].SubItems[ subindex ].Control != null ) )
Items[index].SubItems[ subindex ].Control.Hide();
}
}
else
{
int nXCursor = -this.hPanelScrollBar.Value;
for ( int subindex = 0; subindex < Columns.Count; subindex++ )
{
if ( ( Columns[ subindex ].ControlType != GLColumn.ColumnControlTypes.None ) && ( Items[index].SubItems[ subindex ].Control != null ) )
{
Rectangle rectCtrl = new Rectangle( nXCursor, (nVisibleItem*this.ItemHeight) + this.RowsClientRect.Y, Columns[subindex].Width , this.ItemHeight );
Items[index].SubItems[ subindex ].Control.Bounds = rectCtrl;
Items[index].SubItems[ subindex ].Control.Show();
}
nXCursor += Columns[subindex].Width;
}
nVisibleItem++;
}
}
}
#endif
protected void RecalcScroll( )//Graphics g )
{
DW("RecalcScroll");
// lets add recalc of transparent picture box as well
//this.SuspendLayout();
int nSomethingHasGoneVeryWrongSoBreakOut = 0;
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;
DI("Calling Invalidate From RecalcScroll");
Invalidate();
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;
DI("Calling Invalidate From RecalcScroll");
Invalidate();
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;
DI("Calling Invalidate From RecalcScroll");
Invalidate();
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;
DI("Calling Invalidate From RecalcScroll");
Invalidate();
DW("hiding vscrollbar");
}
DW("End scrolbar updates loop");
// *** WARNING *** WARNING *** Kludge. Not sure why this is sometimes hanging. Fix this.
if ( ++nSomethingHasGoneVeryWrongSoBreakOut > 4 )
break;
} 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 = VisibleRowsCount;
vPanelScrollBar.mMaximum = Count-1;
if ( ((vPanelScrollBar.Value + VisibleRowsCount ) > Count) ) // catch all to make sure the scrollbar isnt going farther than visible items
{
DW("Changing vpanel value");
vPanelScrollBar.Value = Count - VisibleRowsCount; // 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;
}
}
#if true
if ( BorderPadding > 0 )
{
horiBottomBorderStrip.Bounds = new Rectangle( 0, this.ClientRectangle.Bottom-this.BorderPadding, this.ClientRectangle.Width, this.BorderPadding ) ; // horizontal bottom picture box
horiTopBorderStrip.Bounds = new Rectangle( 0, this.ClientRectangle.Top, this.ClientRectangle.Width, this.BorderPadding ) ; // horizontal bottom picture box
vertLeftBorderStrip.Bounds = new Rectangle( 0, 0, this.BorderPadding, this.ClientRectangle.Height ) ; // horizontal bottom picture box
vertRightBorderStrip.Bounds = new Rectangle( this.ClientRectangle.Right-this.BorderPadding, 0, this.BorderPadding, this.ClientRectangle.Height ) ; // horizontal bottom picture box
}
else
{
if ( this.horiBottomBorderStrip.Visible )
this.horiBottomBorderStrip.Visible = false;
if ( this.horiTopBorderStrip.Visible )
this.horiTopBorderStrip.Visible = false;
if ( this.vertLeftBorderStrip.Visible )
this.vertLeftBorderStrip.Visible = false;
if ( this.vertRightBorderStrip.Visible )
this.vertRightBorderStrip.Visible = false;
}
if ( hPanelScrollBar.Visible && vPanelScrollBar.Visible )
{
if ( !cornerBox.Visible )
cornerBox.Visible = true;
cornerBox.Bounds = new Rectangle( hPanelScrollBar.Right, vPanelScrollBar.Bottom, vPanelScrollBar.Width, hPanelScrollBar.Height );
}
else
{
if ( cornerBox.Visible )
cornerBox.Visible = false;
}
#endif
//this.ResumeLayout();
//DW("Exit recalc scroll");
}
private void vPanelScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
{
DW("vPanelScrollBar_Scroll");
//this.Focus();
DI("Calling Invalidate From vPanelScrollBar_Scroll");
Invalidate();
}
private void hPanelScrollBar_Scroll(object sender, System.Windows.Forms.ScrollEventArgs e)
{
DW("hPanelScrollBar_Scroll");
//this.Focus();
DI("Calling Invalidate From hPanelScrollBar_Scroll");
Invalidate();
}
#endregion
#region Mouse
/// <summary>
/// OnDoubleclick
///
/// if someone double clicks on an area, we need to start a control potentially
/// </summary>
/// <param name="e"></param>
protected override void OnDoubleClick(EventArgs e)
{
DW("GlacialList.OnDoubleClick");
Point pointLocalMouse = this.PointToClient( Cursor.Position );
Debug.WriteLine( "Double Click Called" );
Debug.WriteLine( "At Cords X " + pointLocalMouse.X.ToString() + " Y " + pointLocalMouse .Y.ToString() );
base.OnDoubleClick( e );
}
/// <summary>
/// had to put this routine in because of overriden protection level being unchangable
/// </summary>
/// <param name="e"></param>
internal void OnMouseDownFromSubItem( object Sender, MouseEventArgs e )
{
DW("OnMouseDownFromSubItem");
//Debug.WriteLine( "OnMouseDownFromSubItem called " + e.X.ToString() + " " + e.Y.ToString() );
Point cp = this.PointToClient( new Point( Control.MousePosition.X, Control.MousePosition.Y ) );
e = new MouseEventArgs( e.Button, e.Clicks, cp.X, cp.Y, e.Delta );
//Debug.WriteLine( "after " + cp.X.ToString() + " " + cp.Y.ToString() );
OnMouseDown( e );
}
protected override void OnMouseLeave(EventArgs e)
{
this.Columns.ClearHotStates();
base.OnMouseLeave (e);
}
/// <summary>
/// mouse button pressed
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
DW("GlacialList_MouseDown");
Debug.WriteLine( "Real " + e.X.ToString() + " " + e.Y.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 = ColumnStates.csPressed;
if ( ColumnClickedEvent != null )
ColumnClickedEvent( this, new ClickEventArgs( nItem, nColumn ) ); // fire the column clicked event
this.SortColumn( nColumn );
//Invalidate();
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;
this.FocusedItem = Items[nItem];
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( Items[nItem] );
// 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;
}
}
/// <summary>
/// when mouse moves
/// </summary>
/// <param name="e"></param>
protected override void OnMouseMove(MouseEventArgs e)
{
DW("GlacialList_MouseMove");
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() );
}
}
/// <summary>
/// mouse up
/// </summary>
/// <param name="e"></param>
protected override void OnMouseUp(MouseEventArgs e)
{
DW("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 );
m_nState = ListStates.stateNone;
}
#endregion
#endregion // functionality
}
}