Thursday, November 1, 2012

Custom Listview with combox and text box






-->
* ListViewEx.cs
 * This file contains the definition of the class ListViewEx, which is a
 * reusable class derived from ListView.
*/

using System;
using System.Drawing;
using System.Collections;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
using System.Data;

namespace CustomListView
{
/// <summary>
/// Class derived from ListView to give ability to display controls
/// like TextBox and Combobox
/// </summary>
public class ListViewEx : ListView
{

/// <summary>
/// This struct type will be used as the oupput
/// param of the SendMessage( GetSubItemRect ).
/// Actually it is a representation for the strucure
/// RECT in Win32
/// </summary>
[StructLayout(LayoutKind.Sequential)]
internal struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}



/// <summary>
/// Summary description for Win32.
/// </summary>
internal class Win32
{
/// <summary>
/// This is the number of the message for getting the sub item rect.
/// </summary>
public const int LVM_GETSUBITEMRECT  = (0x1000) + 56;

/// <summary>
/// As we are using the detailed view for the list,
/// LVIR_BOUNDS is the best parameters for RECT's 'left' member.
/// </summary>
public const int LVIR_BOUNDS = 0;

/// <summary>
/// Sending message to Win32
/// </summary>
/// <param name="hWnd">Handle to the control</param>
/// <param name="messageID">ID of the message</param>
/// <param name="wParam"></param>
/// <param name="lParam"></param>
/// <returns></returns>
[DllImport("user32.dll", SetLastError=true)]
public static extern int SendMessage(IntPtr hWnd, int messageID, int wParam , ref RECT lParam);
}



/// <summary>
/// This class is used to represent
/// a listview subitem.
/// </summary>
internal class SubItem
{
/// <summary>
/// Item index
/// </summary>
public readonly int row;

/// <summary>
/// Subitem index
/// </summary>
public readonly int col;

/// <summary>
/// Parameterized contructor
/// </summary>
/// <param name="row"></param>
/// <param name="col"></param>
public SubItem( int row, int col )
{
this.row = row;
this.col = col;
}
}



/// <summary>
/// If this variable is true, then
/// subitems for an item is added
/// automatically, if not present.
/// </summary>
private bool addSubItem = false;
public bool AddSubItem
{
set
{
this.addSubItem = value;
}
}

/// <summary>
/// This variable tells whether the combo box
/// is needed to be displayed after its selection
/// is changed
/// </summary>
private bool hideComboAfterSelChange = false;
public bool HideComboAfterSelChange
{
set
{
this.hideComboAfterSelChange = value;
}
}

/// <summary>
/// Represents current row
/// </summary>
private int row = -1;

/// <summary>
/// Represents current column
/// </summary>
private int col = -1;

/// <summary>
/// Textbox to display in the editable cells
/// </summary>
private TextBox textBox = new TextBox();

/// <summary>
/// Combo box to display in the associated cells
/// </summary>
private ComboBox combo = new ComboBox();

/// <summary>
/// This is a flag variable. This is used to determine whether
/// Mousebutton is pressed within the listview
/// </summary>
private bool mouseDown = false;

/// <summary>
/// To store, subitems that contains comboboxes and text boxes
/// </summary>
private Hashtable customCells = new Hashtable();



/// <summary>
/// Constructor
/// </summary>
public ListViewEx()
{
// Initialize controls
this.InitializeComponent();
}

/// <summary>
/// Initializes the text box and combo box
/// </summary>
private void InitializeComponent()
{
// Text box
this.textBox.Visible = false;
textBox.BorderStyle = BorderStyle.FixedSingle;
this.textBox.Leave += new EventHandler(textBox_Leave);

// Combo box
this.combo.Visible = false;
this.Controls.Add( this.textBox );
this.Controls.Add( this.combo );
this.combo.DropDownStyle = ComboBoxStyle.DropDownList;
this.combo.SelectedIndexChanged += new EventHandler(combo_SelectedIndexChanged);

            this.combo.SelectedIndexChanged -= new EventHandler(combo_SelectedIndexChanged);
}

/// <summary>
/// This method will send LVM_GETSUBITEMRECT message to
/// get the current subitem bouds of the listview
/// </summary>
/// <param name="clickPoint"></param>
/// <returns></returns>
private RECT GetSubItemRect(Point clickPoint)
{
// Create output param
RECT subItemRect = new RECT();

// Reset the indices
this.row = this.col = -1;

// Check whether there is any item at the mouse point
ListViewItem item = this.GetItemAt(clickPoint.X, clickPoint.Y);

if(item != null )
{
for(int index = 0; index < this.Columns.Count; index++)
{                                
// We need to pass the 1 based index of the subitem.
subItemRect.top = index + 1;

// To get the boudning rectangle, as we are using the report view
subItemRect.left = Win32.LVIR_BOUNDS;
try
{
// Send Win32 message for getting the subitem rect.
// result = 0 means error occuured
int result = Win32.SendMessage(this.Handle, Win32.LVM_GETSUBITEMRECT, item.Index, ref subItemRect);
if( result != 0 )
{
// This case happens when items in the first columnis selected.
// So we need to set the column number explicitly
if(clickPoint.X < subItemRect.left)
{
this.row = item.Index;
this.col= 0;
break;
}
if(clickPoint.X >= subItemRect.left & clickPoint.X <=
subItemRect.right)
{
this.row = item.Index;
// Add 1 because of the presence of above condition
this.col = index + 1;
break;
}
}
else
{
// This call will create a new Win32Exception with the last Win32 Error.
throw new Win32Exception();
}
}
catch( Win32Exception ex )
{
Trace.WriteLine( string.Format("Exception while getting subitem rect, {0}", ex.Message ));
}
}
}                        
return subItemRect;
}

/// <summary>
/// Set a text box in a cell
/// </summary>
/// <param name="row">The 0-based index of the item.  Give -1 if you
///                                          want to set a text box for every items for a
///                                          given "col" variable.
///        </param>
/// <param name="col">The 0-based index of the column. Give -1 if you
///                                          want to set a text box for every subitems for a
///                                          given "row" variable.
///        </param>
public void AddEditableCell( int row, int col )
{
// Add the cell into the hashtable
// Value is setting as null because it is an editable cell
this.customCells[new SubItem( row, col )] = null;
}

/// <summary>
/// Set a combobox in a cell
/// </summary>
/// <param name="row"> The 0-based index of the item.  Give -1 if you
///                                           want to set a combo box for every items for a
///                                           given "col" variable.
///        </param>
/// <param name="col"> The 0-based index of the column. Give -1 if you
///                                           want to set a combo box for every subitems for a
///                                           given "row" variable.
///        </param>
/// <param name="data"> Items of the combobox
/// </param>
public void AddComboBoxCell( int row, int col, StringCollection data )
{
// Add the cell into the hashtable
// Value for the hashtable is the combobox items
this.customCells[new SubItem( row, col )] = data;
}

/// <summary>
/// Set a combobox in a cell
/// </summary>
/// <param name="row"> The 0-based index of the item.  Give -1 if you
///                                           want to set a combo box for every items for a
///                                           given "col" variable.
///        </param>
/// <param name="col"> The 0-based index of the column. Give -1 if you
///                                           want to set a combo box for every subitems for a
///                                           given "row" variable.
///        </param>
/// <param name="data"> Items of the combobox
/// </param>
public void AddComboBoxCell( int row, int col,DataTable data)
{
try
{
StringCollection param = new StringCollection();
                for (int i = 0; i < data.Rows.Count; i++)
                    param.Add(data.Rows[i]["Name"].ToString());
this.AddComboBoxCell( row, col, param );
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

/// <summary>
/// This method will display the combobox
/// </summary>
/// <param name="location">Location of the combobox</param>
/// <param name="sz">Size of the combobox</param>
/// <param name="data">Combobox items</param>
private void ShowComboBox( Point location, Size sz, StringCollection data )
{
try
{
// Initialize the combobox
combo.Size = sz;
combo.Location = location;
// Add items
combo.Items.Clear();
foreach( string text in data )
{
combo.Items.Add( text );
}                        
// Set the current text, take it from the current listview cell
combo.Text = this.Items[row].SubItems[col].Text;
// Calculate and set drop down width
combo.DropDownWidth = this.GetDropDownWidth(data);
// Show the combo
combo.Show();
}
catch( ArgumentOutOfRangeException )
{
// Sink
}
}

/// <summary>
/// This method will display the textbox
/// </summary>
/// <param name="location">Location of the textbox</param>
/// <param name="sz">Size of the textbox</param>
private void ShowTextBox( Point location, Size sz )
{
try
{
// Initialize the textbox
textBox.Size = sz;
textBox.Location = location;
// Set text, take it from the current listview cell
textBox.Text = this.Items[row].SubItems[col].Text;
// Shopw the text box
textBox.Show();
textBox.Focus();
}
catch( ArgumentOutOfRangeException )
{
// Sink
}
}

/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnMouseUp ( MouseEventArgs e)
{
try
{
// Hide the controls
this.textBox.Visible = this.combo.Visible = false;

// If no mouse down happned in this listview,
// no need to show anything
if( !mouseDown )
{
return;
}

// The listview should be having the following properties enabled
// 1. FullRowSelect = true
// 2. View should be Detail;
if( !this.FullRowSelect || this.View != View.Details )
{
return;
}

// Reset the mouse down flag
mouseDown = false;

// Get the subitem rect at the mouse point.
// Remeber that the current row index and column index will also be
// Modified within the same method
RECT rect = this.GetSubItemRect( new Point( e.X, e.Y ));

// If the above method is executed with any error,
// The row index and column index will be -1;
if( this.row != -1 && this.col != -1 )
{
// Check whether combobox or text box is set for the current cell
SubItem cell = GetKey(new SubItem(this.row, this.col ));

if( cell != null)
{
// Set the size of the control(combobox/editbox)
// This should be composed of the height of the current items and
// width of the current column
Size sz = new Size(this.Columns[col].Width, Items[row].Bounds.Height );

// Determine the location where the control(combobox/editbox) to be placed
Point location = col == 0 ? new Point(0, rect.top) : new Point( rect.left, rect.top);

ValidateAndAddSubItems();

// Decide which conrol to be displayed.
if(this.customCells[cell] == null )
{
this.ShowTextBox( location, sz );
}
else
{
this.ShowComboBox( location, sz, (StringCollection) this.customCells[cell] );
}
}
}
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

/// <summary>
///
/// </summary>
private void ValidateAndAddSubItems()
{
try
{
while( this.Items[this.row].SubItems.Count < this.Columns.Count && this.addSubItem )
{
this.Items[this.row].SubItems.Add("");
}
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

/// <summary>
/// This message will get the largest text from the given
/// stringarray, and will calculate the width of a control which
/// will contain that text.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private int GetDropDownWidth( StringCollection data )
{
// If array is empty just return the combo box width
if( data.Count == 0 )
{
return this.combo.Width;
}

// Set the first text as the largest
string maximum = data[0];

// Now iterate thru each string, to findout the
// largest
foreach( string text in data )
{
if( maximum.Length <  text.Length )
{
maximum = text;
}
}
// Calculate and return the width .
return (int)(this.CreateGraphics().MeasureString( maximum , this.Font ).Width);
}

/// <summary>
/// For this method, we will get a Subitem.
/// Then we will iterate thru each of the keys and will
/// check whther any key contains the given cells row/column.
/// If it is not found we will check for -1 in any one
/// </summary>
/// <param name="cell"></param>
/// <returns></returns>
private SubItem GetKey( SubItem cell )
{
try
{
foreach( SubItem key in this.customCells.Keys )
{
// Case 1: Any particular cells is  enabled for a control(Textbox/combobox)
if( key.row == cell.row && key.col == cell.col )
{
return key;
}
// Case 2: Any particular column is  enabled for a control(Textbox/combobox)
else if( key.row == -1 && key.col == cell.col )
{
return key;
}
// Entire col for a row is is  enabled for a control(Textbox/combobox)
else if( key.row == cell.row && key.col == -1 )
{
return key;
}
// All cells are enabled for a control(Textbox/combobox)
else if( key.row == -1 && key.col == -1 )
{
return key;
}
}
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
return null;
}

/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(MouseEventArgs e)
{
try
{
// Mouse down happened inside listview
mouseDown = true;

// Hide the controls
this.textBox.Hide();
this.combo.Hide();
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

/// <summary>
/// This event handler wll set the current text in the textbox
/// as the listview's current cell's text, while the textbox
/// focus is lost
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void textBox_Leave(object sender, EventArgs e)
{
try
{
if( this.row != -1 && this.col != -1 )
{
this.Items[row].SubItems[col].Text = this.textBox.Text;
this.textBox.Hide();
}
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

/// <summary>
/// This event handler wll set the current text in the combobox
/// as the listview's current cell's text, while the combobox
/// selection is changed
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void combo_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
if( this.row != -1 && this.col != -1 )
{
this.Items[row].SubItems[col].Text = this.combo.Text;
this.combo.Visible = !this.hideComboAfterSelChange;                
}
}
catch( Exception ex )
{
Trace.WriteLine( ex.ToString());
}
}

}
}


Form.cs

using CustomListView;

private ListViewEx listViewMain;
 private void Form1_Load(object sender, System.EventArgs e)
        {
            // If subitem is not added, add it automatically on clicking an item.
            this.listViewMain.AddSubItem = true;

            // Make the Name and adress columns editable
            this.listViewMain.AddEditableCell(-1, 0);
            this.listViewMain.AddEditableCell(-1, 2);

            // Create data for combobox
            StringCollection grades = new StringCollection();
            grades.AddRange(new string[] { "A", "B", "C", "D", "E" });

            DataTable dt = new DataTable();
            dt.Columns.Add("Name");
            dt.Columns.Add("Value");
            DataRow dr = dt.NewRow();
            dr["Name"] = "Sanjay";
            dr["Value"] = "1";
            dt.Rows.Add(dr);

            dr = dt.NewRow();
            dr["Name"] = "Raju";
            dr["Value"] = "2";
            dt.Rows.Add(dr);

            // Set the combobox
            this.listViewMain.AddComboBoxCell(-1, 1, dt);
        }



If you are searching life partner. your searching end with kpmarriage.com. now kpmarriage.com offer free matrimonial website which offer free message, free chat, free view contact information. so register here : kpmarriage.com- Free matrimonial website