提供数据绑定和排序功能的ListView完全解决方案

在论坛上经常看到有初学者问,ListView列排序的时候列上面怎么样出现一个小三角的图标,今天花了一个上午封装了ListView控件,并提供数据绑定的功能。源代码如下:
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Resources;
using System.Reflection;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Data ;
 
namespace ListViewEx
{
 #region Enumerations
 public enum SortedFormatType
 {
  String,
  Numeric,
  Date,
  Custom
 }

 public enum SortedDirection
 {
  Ascending,
  Descending
 }
 #endregion

 #region 事件处理定义...
 public delegate void ListViewExEventHandler(object sender, ListViewExEventArgs e);
 /// <summary>
 ///在生成Listview 事件 需要的自定义参数。
 /// </summary>
 public class ListViewExEventArgs : EventArgs
 {
  private string mFiledName;
  private string mSubItemText;
  
  public ListViewExEventArgs(string pFieldName,string pSubItemText)
  {
   mFiledName = pFieldName;
   mSubItemText = pSubItemText;
  }
  #region Public 属性 ...
  public string SubItemText
  {
   get
   {
    return mSubItemText;
   }
   set
   {
    mSubItemText=value;
   }
  }
  #endregion Public 属性 ...
 }

 #endregion 事件处理定义...
 /// <summary>
 /// 继承ListView 并增加数据绑定和排序的功能
 /// </summary>
 [ToolboxItemFilter("System.Windows.Forms")]
 [ToolboxBitmap(typeof(ListViewEx),"ListViewEx.bmp")]
 public class ListViewEx : ListView
 {
  private DataSet mDataSouce;
  private int mKeyIndex = -1 ;
  private string mKeyName = "ID";
  private SortColumnType[] mColType;
  private ListViewSortManager mSortManager;
  private int mMaxRowCount = 1000;

  #region 事件处理相关...
  private ListViewExEventHandler mBeforAddCell;
  public event ListViewExEventHandler BeforAddCell
  {
   add
   {
    mBeforAddCell += value;
   }
   remove
   {
    mBeforAddCell -= value;
   }

  }
  private void FireBeforAddCell(ListViewExEventArgs e)
  {
   if (mBeforAddCell!= null)
   {
    // 调用相应的委托代理
    mBeforAddCell(this, e);
   }
  }
  #endregion 事件处理相关...

  public ListViewEx()
  {      
   FullRowSelect = true;
   View = View.Details;
   this.CheckBoxes = true;
  }
  //绑定DataSet
  public DataSet DataSouce
  {
   set
   {
    mDataSouce = value;
    refresh();
   }
  }

  #region 创建 ListView 的表头和子项...
  private void refresh()
  {
   Debug.Assert(mDataSouce!=null,"显示ListView","显示ListView的时候不能设置一个空的DataSet!");
   createColumns() ;
   createListItem();
   //增加排序的处理
   mSortManager = new ListViewSortManager(this,mColType);
  }
  //创建 List Item
  private void createListItem()
  {
   DataTable dt =  mDataSouce.Tables[0];
   if(this.Items.Count>0)
    this.Items.Clear();
   int colCount = dt.Columns.Count;
   int curRowIndex = 0;
   foreach(DataRow dr in dt.Rows)
   {
    //目前设置最大值是1000,翻页处理以后再添加
    if(curRowIndex > MaxRowCount)
     break;
    //先增加Key 值列
    int itemIndex = 0;
    if( mKeyIndex ==0)
    {
     itemIndex =1;
    }
    string cellText = dr[itemIndex].ToString();
    ListViewExEventArgs arg = new ListViewExEventArgs( dt.Columns[itemIndex].ColumnName,cellText);
    FireBeforAddCell(arg);
    ListViewItem item = new ListViewItem(arg.SubItemText,0);
    //把Table 的行存在Item 的tag 属性中
    item.Tag = dr;
    for(int i=itemIndex ; i< mKeyIndex;i++)
    {
     cellText = dr[i].ToString();
     arg = new ListViewExEventArgs( dt.Columns[i].ColumnName,cellText);
     FireBeforAddCell(arg);
     item.SubItems.Add(arg.SubItemText);
    }
    for(int i=mKeyIndex +1;i<colCount ;i++)
    {
     cellText = dr[i].ToString();
     arg = new ListViewExEventArgs( dt.Columns[i].ColumnName,cellText);
     FireBeforAddCell(arg);
     item.SubItems.Add(arg.SubItemText);
    }
    curRowIndex ++;
    this.Items.Add(item) ;
   }
  }
  //创建ListView的表头
  private void createColumns()
  {
   DataTable dt =  mDataSouce.Tables[0];
   if(Columns.Count > 0)
    Columns.Clear();
   int i = 0;
   mColType = new SortColumnType[dt.Columns.Count];
   foreach(DataColumn dc in dt.Columns)
   {
    //HorizontalAlignment.Left
    //得到该Table 中是否有内置的Key Index 并得到它
    if(i!=mKeyIndex)
    {
     if(dc.ColumnName.ToUpper()  == mKeyName)
     {
      //this.Columns.Add(dc.Caption, 0,getAlignment(dc,i));
      mKeyIndex = i;
     }
     else
     {
      this.Columns.Add(dc.Caption,100,getAlignment(dc,i));
     }
    }
    i++;
   }
  }
  //得到列的文本的对齐方式
  private HorizontalAlignment getAlignment(DataColumn pDc,int pColIndex)
  {
   switch(pDc.GetType().Name)
   {
    case "String":
     mColType[pColIndex] = SortColumnType.Text ;
     return HorizontalAlignment.Left;
    case "Int16" :
    case "Int32" :
    case "Decimal":
     mColType[pColIndex] = SortColumnType.Number;
     return HorizontalAlignment.Right;
    case "DateTime" :
     mColType[pColIndex] = SortColumnType.DateTime;
     return HorizontalAlignment.Left;
    default:
     mColType[pColIndex] = SortColumnType.Text ;
     return HorizontalAlignment.Left;
   }
  }
  #endregion 创建 ListView 的表头和子项...

  #region Public 属性 ...
  public int KeyIndex
  {
   get
   {
    return mKeyIndex;
   }
   set
   {
    mKeyIndex = value;
   }
  }
  public string KeyName
  {
   get
   {
    return mKeyName;
   }
   set
   {
    mKeyName = value;
   }
  }
  public int MaxRowCount
  {
   get
   {
    return mMaxRowCount;
   }
   set
   {
    mMaxRowCount = value;
   }
  }
  #endregion Public 属性 ...
 }


 #region  ListViewSortManager ...
 enum SortColumnType
 {
  Text,
  Number,
  DateTime
 }
 /// <summary>
 /// 继承IComparer接口,提供两个文本之间的比较方法
 /// </summary>
 class ListViewTextSort: IComparer
 {
  private SortColumnType mType;
  private Int32 mColumn;
  private Boolean mAscending;
  public  SortColumnType CompareType
  {
   set
   {
    mType = value;
   }
  }
  public ListViewTextSort(Int32 sortColumn, Boolean ascending)
  {
   mColumn = sortColumn;
   mAscending = ascending;
  }

  /// <summary>
  /// 实现IComparer.Compare
  /// </summary>
  /// <param name="lhs">比较的第一个对象</param>
  /// <param name="rhs">比较的第二个对象</param>
  /// <returns>  如果lhs 小于 rhs那么小于零,否则大于零,或者等于零</returns>
  public Int32 Compare(Object lhs, Object rhs)
  {
   ListViewItem lhsLvi = lhs as ListViewItem;
   ListViewItem rhsLvi = rhs as ListViewItem;

   if(lhsLvi == null || rhsLvi == null)  
    return 0;

   ListViewItem.ListViewSubItemCollection lhsItems = lhsLvi.SubItems;
   ListViewItem.ListViewSubItemCollection rhsItems = rhsLvi.SubItems;

   String lhsText = (lhsItems.Count > mColumn) ? lhsItems[mColumn].Text : String.Empty;
   String rhsText = (rhsItems.Count > mColumn) ? rhsItems[mColumn].Text : String.Empty;

   Int32 result = 0;
   if(lhsText.Length == 0 || rhsText.Length == 0)
    result = lhsText.CompareTo(rhsText);

   else
    result = OnCompare(lhsText, rhsText);

   if(!mAscending)
    result = -result;

   return result;
  }

  /// <summary>
  /// 覆盖进行特别类型的比较
  /// </summary>
  /// <param name="lhs">比较的第一个对象</param>
  /// <param name="rhs">比较的第二个对象</param>
  /// <returns>  如果lhs 小于 rhs那么小于零,否则大于零,或者等于零</returns>
  protected virtual Int32 OnCompare(String lhs, String rhs)
  {
   switch(mType)
   {
    case SortColumnType.Text :
     return String.Compare(lhs, rhs, false);
    case SortColumnType.Number :
     Double result = Double.Parse(lhs) - Double.Parse(rhs);

     if(result > 0)
      return 1;
     else if(result < 0)
      return -1;
     else
      return 0;
    case SortColumnType.DateTime :
     return DateTime.Parse(lhs).CompareTo(DateTime.Parse(rhs));
    default :
     return String.Compare(lhs, rhs, false);
   }
  }
 }

 /// <summary>
 /// 和ListViewEx分离,实现列排序的管理
 /// </summary>
 class ListViewSortManager
 {
  private Int32 mColumn;
  private SortOrder mSortOrder;
  private ListViewEx mList;
  private SortColumnType[] mComparers;
  private ImageList mImgList;

  /// <param name="list">管理列排序的ListViewEx对象</param>
  /// <param name="comparers">对应每一列排序的类型</param>
  public ListViewSortManager(ListViewEx list, SortColumnType[] comparers)
  {
   mColumn = -1;
   mSortOrder = SortOrder.None;

   mList = list;
   mComparers = comparers;

   mImgList = new ImageList();
   mImgList.ImageSize = new Size(8, 8);
   mImgList.TransparentColor = System.Drawing.Color.Magenta;

   mImgList.Images.Add(GetArrowBitmap(ListSortDirection.Ascending));  
   mImgList.Images.Add(GetArrowBitmap(ListSortDirection.Descending));  

   SetHeaderImageList(mList, mImgList);

   list.ColumnClick += new ColumnClickEventHandler(ColumnClick);
  }

  /// <summary>
  /// 当前排序的列的Index
  /// </summary>
  public Int32 Column
  {
   get { return mColumn; }
  }

  /// <summary>
  /// 当前排序的方式
  /// </summary>
  public SortOrder SortOrder
  {
   get { return mSortOrder; }
  }

  /// <summary>
  /// 排序行
  /// </summary>
  public void Sort(Int32 column)
  {
   SortOrder order = SortOrder.Ascending;
   
   if(column == mColumn)
    order = (mSortOrder == SortOrder.Ascending) ? SortOrder.Descending : SortOrder.Ascending;

   Sort(column, order);
  }
  public void Sort(Int32 column, SortOrder order)
  {
   if(column < 0 || column >= mComparers.Length)
    throw new IndexOutOfRangeException();

   if(column != mColumn)
   {
    ShowHeaderIcon(mList, mColumn, SortOrder.None);
    mColumn = column;
   }

   ShowHeaderIcon(mList, mColumn, order);
   mSortOrder = order;

   if(order != SortOrder.None)
   {
    ListViewTextSort comp = new ListViewTextSort(mColumn, order == SortOrder.Ascending);//(ListViewTextSort) Activator.CreateInstance(mComparers[mColumn], new Object[] { mColumn, order == SortOrder.Ascending } );
    comp.CompareType = mComparers[mColumn];
    mList.ListViewItemSorter = comp;
   }
  }
  //当Click ListViewEx列标题时,得到它的事件,并进行排序的处理
  private void ColumnClick(object sender, ColumnClickEventArgs e)
  {
   this.Sort(e.Column);       
  }


  #region Graphics
  //画向下或者向上的方向箭头
  Bitmap GetArrowBitmap(ListSortDirection  type)
  {
   Bitmap bmp = new Bitmap(8, 8);
   Graphics gfx = Graphics.FromImage(bmp);

   Pen lightPen = SystemPens.ControlLightLight;
   Pen shadowPen = SystemPens.ControlDark;

   gfx.FillRectangle(System.Drawing.Brushes.Magenta, 0, 0, 8, 8);

   if(type == ListSortDirection.Ascending)  
   {
    gfx.DrawLine(lightPen, 0, 7, 7, 7);
    gfx.DrawLine(lightPen, 7, 7, 4, 0);
    gfx.DrawLine(shadowPen, 3, 0, 0, 7);
   }

   else if(type == ListSortDirection.Descending)
   {
    gfx.DrawLine(lightPen, 4, 7, 7, 0);
    gfx.DrawLine(shadowPen, 3, 7, 0, 0);
    gfx.DrawLine(shadowPen, 0, 0, 7, 0);
   }
   
   gfx.Dispose();

   return bmp;
  }
  #region WIndows32 API...
  [StructLayout(LayoutKind.Sequential)]
   private struct HDITEM
  {
   public Int32     mask;
   public Int32     cxy;  
   [MarshalAs(UnmanagedType.LPTStr)]
   public String    pszText;
   public IntPtr  hbm;
   public Int32     cchTextMax;
   public Int32     fmt;
   public Int32     lParam;
   public Int32     iImage;
   public Int32     iOrder;
  };

  [DllImport("user32")]
  static extern IntPtr SendMessage(IntPtr Handle, Int32 msg, IntPtr wParam, IntPtr lParam);

  [DllImport("user32", EntryPoint="SendMessage")]
  static extern IntPtr SendMessage2(IntPtr Handle, Int32 msg, IntPtr wParam, ref HDITEM lParam);

  const Int32 HDI_WIDTH   = 0x0001;
  const Int32 HDI_HEIGHT   = HDI_WIDTH;
  const Int32 HDI_TEXT   = 0x0002;
  const Int32 HDI_FORMAT   = 0x0004;
  const Int32 HDI_LPARAM   = 0x0008;
  const Int32 HDI_BITMAP   = 0x0010;
  const Int32 HDI_IMAGE   = 0x0020;
  const Int32 HDI_DI_SETITEM  = 0x0040;
  const Int32 HDI_ORDER   = 0x0080;
  const Int32 HDI_FILTER   = 0x0100;  // 0x0500

  const Int32 HDF_LEFT   = 0x0000;
  const Int32 HDF_RIGHT   = 0x0001;
  const Int32 HDF_CENTER   = 0x0002;
  const Int32 HDF_JUSTIFYMASK  = 0x0003;
  const Int32 HDF_RTLREADING  = 0x0004;
  const Int32 HDF_OWNERDRAW  = 0x8000;
  const Int32 HDF_STRING   = 0x4000;
  const Int32 HDF_BITMAP   = 0x2000;
  const Int32 HDF_BITMAP_ON_RIGHT = 0x1000;
  const Int32 HDF_IMAGE   = 0x0800;
  const Int32 HDF_SORTUP   = 0x0400;  // 0x0501
  const Int32 HDF_SORTDOWN  = 0x0200;  // 0x0501

  const Int32 LVM_FIRST           = 0x1000;  // List 消息
  const Int32 LVM_GETHEADER  = LVM_FIRST + 31;

  const Int32 HDM_FIRST           = 0x1200;  // Header 消息
  const Int32 HDM_SETIMAGELIST = HDM_FIRST + 8;
  const Int32 HDM_GETIMAGELIST    = HDM_FIRST + 9;
  const Int32 HDM_GETITEM   = HDM_FIRST + 11;
  const Int32 HDM_SETITEM   = HDM_FIRST + 12;
  #endregion WIndows32 API...
  //显示Header的图标
  private void ShowHeaderIcon(ListView list, int columnIndex, SortOrder sortOrder)
  {
   if(columnIndex < 0 || columnIndex >= list.Columns.Count)
    return;

   IntPtr hHeader = SendMessage(list.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);

   ColumnHeader colHdr = list.Columns[columnIndex];

   HDITEM hd = new HDITEM();
   hd.mask = HDI_IMAGE | HDI_FORMAT;

   HorizontalAlignment align = colHdr.TextAlign;

   if(align == HorizontalAlignment.Left)
    hd.fmt = HDF_LEFT | HDF_STRING | HDF_BITMAP_ON_RIGHT;

   else if(align == HorizontalAlignment.Center)
    hd.fmt = HDF_CENTER | HDF_STRING | HDF_BITMAP_ON_RIGHT;

   else // HorizontalAlignment.Right
    hd.fmt = HDF_RIGHT | HDF_STRING;

   if(sortOrder != SortOrder.None)
    hd.fmt |= HDF_IMAGE;

   hd.iImage = (int) sortOrder - 1;

   SendMessage2(hHeader, HDM_SETITEM, new IntPtr(columnIndex), ref hd);
  }

  private void SetHeaderImageList(ListView list, ImageList imgList)
  {
   IntPtr hHeader = SendMessage(list.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
   SendMessage(hHeader, HDM_SETIMAGELIST, IntPtr.Zero, imgList.Handle);
  }
  #endregion Graphics
 }
 #endregion  ListViewSortManager ...
 
}

posted on 2005-08-09 15:59  笑天  阅读(1648)  评论(0编辑  收藏  举报

导航