⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 listviewfilter.cs

📁 This control is another extension to the now standard and widely used ListView control. I have inclu
💻 CS
📖 第 1 页 / 共 4 页
字号:
    

    /// <summary>
    /// Create a new filter entry for the given column.  This
    /// will replace/add a LVFFilter instance for the column
    /// to the itm_filtrs array for use by FilterUpdate().
    /// </summary>
    /// <param name="c">Column number</param>
    private void FilterBuild( int c )
    {
      // if this column exists in the filters array remove it
      foreach( LVFFilter f in itm_filtrs )
        if ( f.Column == c )
        {
          itm_filtrs.Remove( f );
          break;
        }

      // get the filter text for this column from the header
      string s = hdr_contrl.Filter[ c ].Trim();

      // if there is any size to it then create a new filter
      if ( s.Length > 0 )
      {

        // create a new filter object for this column
        LVFFilter f = new LVFFilter();
        f.Column    = c;
        f.Text      = s;
        f.Compare   = LVFFilterType.Equal;
        f.Type      = hdr_contrl.DataType[ c ];

        // check the first characters of the string to see
        // if this is not a default equality comparison
        switch ( s[ 0 ] )
        {
          case '=':
            f.Text = s.Remove( 0, 1 );
            break;
          case '!':
            f.Compare = LVFFilterType.NotEqual;
            f.Text = s.Remove( 0, 1 );
            break;
          case '>':
            if ( ( s.Length > 1 ) && ( s[ 1 ] == '=' ) )
            {
              f.Compare = LVFFilterType.GreaterEqual;
              f.Text = s.Remove( 0, 2 );
            }
            else
            {
              f.Compare = LVFFilterType.Greater;
              f.Text = s.Remove( 0, 1 );
            }
            break;
          case '<':
            if ( ( s.Length > 1 ) && ( s[ 1 ] == '=' ) )
            {
              f.Compare = LVFFilterType.LessEqual;
              f.Text = s.Remove( 0, 2 );
            }
            else
            {
              f.Compare = LVFFilterType.Less;
              f.Text = s.Remove( 0, 1 );
            }
            break;
        }

        // add this to the array of filters
        itm_filtrs.Add( f );

      }
    }


    /// <summary>
    /// Check a compare result against a filter type.  We use
    /// the internal CompareData and ItemText methods to get
    /// the correct text and perform the comparison using the
    /// filter column datatype.  The result from CompareData
    /// is used to check against the Type of filtering applied.
    /// The f (LVFFilter) has been prepared with all the data
    /// about the filtering to be performed; column, text,
    /// datatype, and filtering type by FilterBuild().
    /// </summary>
    /// <param name="r">Result -1/0/1</param>
    /// <param name="f">LVFFilter</param>
    /// <returns></returns>
    private bool FilterCheck( ListViewItem i, LVFFilter f )
    {

      // get the result of the data type comparison
      int r = ( flt_ignore )
        ? CompareData( ItemText( i, f.Column ).ToLower(), f.Text.ToLower(), f.Type, true )
        : CompareData( ItemText( i, f.Column ), f.Text, f.Type, true );

      // compare the result against the filter comparison type
      // a true result means that it passed filtering and should
      // be left in the list of filtered items.
      switch ( f.Compare )
      {
        case LVFFilterType.Equal:
          return ( r == 0 );
        case LVFFilterType.NotEqual:
          return ( r != 0 );
        case LVFFilterType.Greater:
          return ( r > 0 );
        case LVFFilterType.GreaterEqual:
          return ( r >= 0 );
        case LVFFilterType.Less:
          return ( r < 0 );
        case LVFFilterType.LessEqual:
          return ( r <= 0 );    
        default:
          return ( r == 0 );
      }
    }

    
    /// <summary>
    /// Update the Items list since a filter has changed.  This
    /// is done whenever a filter text has changed, the filter
    /// options change (case or datatype).  This is how we remove
    /// items from the ListView.Items[] and put them into a private
    /// ArrayList of 'filtered' items.  The items moved to the
    /// itm_filtrd array will be put back when filters are changed
    /// or disabled.  There is a problem with this however.  The
    /// ListViewItemsCollection has a real hard time processing
    /// many additions to the list.  When a large number of items
    /// are added back in, it takes a long time.  If there is 
    /// another way to Add (I tried AddRange) ListViewItem(s) to
    /// the collection that is faster, I would really like to know.
    /// </summary>
    private void FilterUpdate()
    {
      // halt all updates to the list while we are working
      this.BeginUpdate();

      // set a flag to say that no change has occurred
      // so that we don't unnecessarily sort the items
      bool c = false;

      // if there are any filters applied, process them.
      // Items removed are not put into the itm_filtrd 
      // list at this time since we would then have to
      // walk them again in the second loop.  instead,
      // they are added to a holding array (itm_holder)
      // which is later copied into the itm_filtrd list.
      // NOTE: we walk the Items list first, then check
      // each itm_filtrs against that one item.  That is
      // much faster than walking the Items list for each
      // itm_filtrs entry, since there is normally only
      // one or two columns filtered at any time.
      if ( itm_filtrs.Count != 0 )
      {

        // we will use a listviewitem instance here
        ListViewItem l;

        // prepare the held out items array to contain items
        // removed this pass so we don't check them twice
        itm_holder.Clear();

        // check each item in the Items list to see if it
        // should be removed to the filtered list
        for ( int i = 0; i < this.Items.Count; ++i )
        {

          // get the item to check against the filters
          l = this.Items[ i ];

          // any filter failure removes from it from the
          // Items list and adds it to the holding list.
          // NOTE: the index (i) is decremented when we
          // remove an item since the list is compressed.
          foreach( LVFFilter f in itm_filtrs )
            if ( !FilterCheck( l, f ) )
            {
              itm_holder.Add( l );
              this.Items.RemoveAt( i-- );
              c = true;
              break;
            }
        }

        // check each item in the currently filtered array
        // to see if it should be put back in the items now.
        // NOTE: this is the slow part when many items need
        // to be put back into the ListViewItemsCollection...
        for ( int i = 0; i < itm_filtrd.Count; ++i )
        {

          // get the object as a listviewitem
          l = (ListViewItem)itm_filtrd[ i ];

          // set no filter failure flag this item
          bool r = true;

          // all filters must pass, stop at one failure
          foreach( LVFFilter f in itm_filtrs )
            if ( !FilterCheck( l, f ) )
            {
              r = false;
              break;
            }

          // if we did not fail any filter put it back in Items
          // and remove it from the filtered items: itm_filtrd.
          // NOTE: the index (i) is decremented when we
          // remove an item since the list is compressed.
          if ( r )
          {
            this.Items.Add( l );
            itm_filtrd.RemoveAt( i-- );
            c = true;
          }
        }

        // now add all the holder items into the filtered list
        // and empty the held out items array to remove reference
        itm_filtrd.AddRange( itm_holder );
        itm_holder.Clear();

      }

      // no filters active put any filtered items back into
      // the items list and empty the filtered items array
      else 
      {
        // set the do sort flag only if there are entries
        c = ( itm_filtrd.Count > 0 );

        // put all filtered items back and clear the list
        foreach( ListViewItem l in itm_filtrd ) Items.Add( l );
        itm_filtrd.Clear();

      }

      // resort the items if the item content has changed
      if ( c ) this.Sort();

      // ensure that updates are re-enabled
      this.EndUpdate();
    }


    /// <summary>
    /// MenuItem click event.  Perform the requested menu action
    /// and always force a filter rebuild since all these change
    /// the filtering properties in some manner.  The sender
    /// identifies the specific menuitem clicked.
    /// </summary>
    /// <param name="sender">MenuItem</param>
    /// <param name="e">Event</param>
    private void MenuItemClick(object sender, System.EventArgs e)
    {

      // 'Clear filter', set the Header.Filter for the column
      if ( sender == mnu_clearf )
        hdr_contrl.Filter[ mnu_column ] = "";

      // 'Ignore case', toggle the flag
      else if ( sender == mnu_ignore )
        flt_ignore = !flt_ignore;

      // 'String', set the datatype comparison
      else if ( sender == mnu_strflt )
        hdr_contrl.DataType[ mnu_column ] = LVFDataType.String;

      // 'Number', set the datatype comparison
      else if ( sender == mnu_nbrflt )
        hdr_contrl.DataType[ mnu_column ] = LVFDataType.Number;

      // 'Date', set the datatype comparison
      else if ( sender == mnu_datflt )
        hdr_contrl.DataType[ mnu_column ] = LVFDataType.Date;

      // 'Left', set the alignment
      else if ( sender == mnu_alignl )
        hdr_contrl.Alignment[ mnu_column ] = HorizontalAlignment.Left;

      // 'Right', set the alignment
      else if ( sender == mnu_alignr )
        hdr_contrl.Alignment[ mnu_column ] = HorizontalAlignment.Right;

      // 'Center', set the alignment
      else if ( sender == mnu_alignc )
        hdr_contrl.Alignment[ mnu_column ] = HorizontalAlignment.Center;

      // unknown, ignore this type
      else return;

      // force a filter build on the specific column
      FilterBuild( mnu_column );

      // follow with a filter update
      FilterUpdate();

      // set focus to ourself after menu action otherwise
      // focus is left in the filter edit itself...
      this.Focus();

      // if this was an alignment change then we need to invalidate
      if ( ((MenuItem)sender).Parent == mnu_alignt ) this.Invalidate();

    }


    /// <summary>
    /// Context menu popup event.  Set context menu item states
    /// for the current column data type and case sensitivity.
    /// </summary>
    /// <param name="sender">ContextMenu</param>
    /// <param name="e">Event</param>
    private void ContextMenuPopup(object sender, System.EventArgs e)
    {

      // set the correct radio menu item based upon the data type
      // NOTE: this is bad, .NET should know how to treat radio
      // items by using the WS_GROUP style in the control...
      switch ( hdr_contrl.DataType[ mnu_column ] )
      {
        case LVFDataType.Date:
          mnu_strflt.Checked = false;
          mnu_nbrflt.Checked = false;
          mnu_datflt.Checked = true;
          break;
        case LVFDataType.Number:
          mnu_strflt.Checked = false;
          mnu_nbrflt.Checked = true;
          mnu_datflt.Checked = false;
          break;
        default:
          mnu_strflt.Checked = true;
          mnu_nbrflt.Checked = false;
          mnu_datflt.Checked = false;
          break;
      }

      // disable the checked DataType menu item
      mnu_strflt.Enabled = !mnu_strflt.Checked;
      mnu_nbrflt.Enabled = !mnu_nbrflt.Checked;
      mnu_datflt.Enabled = !mnu_datflt.Checked;

      // also set the current alignment
      switch ( hdr_contrl.Alignment[ mnu_column ] )
      {
        case HorizontalAlignment.Center:
          mnu_alignl.Checked = false;
          mnu_alignr.Checked = false;
          mnu_alignc.Checked = true;
          break;
        case HorizontalAlignment.Right:
          mnu_alignl.Checked = false;
          mnu_alignr.Checked = true;
          mnu_alignc.Checked = false;
          break;
        default:
          mnu_alignl.Checked = true;
          mnu_alignr.Checked = false;
          mnu_alignc.Checked = false;
          break;
      }

      // disable the checked DataType menu item
      mnu_alignl.Enabled = !mnu_alignl.Checked;
      mnu_alignr.Enabled = !mnu_alignr.Checked;
      mnu_alignc.Enabled = !mnu_alignc.Checked;

      // set the checked state of the case insensitive
      mnu_ignore.Checked = flt_ignore;

    }


    #endregion

    #region Internal methods and data
      
    /// <summary>
    /// Return the Text of an item/subitem as a string.  This is
    /// done to simplify text retrieval in multiple places.  It
    /// would be nice if the ListViewItem itself had this...
    /// </summary>
    /// <param name="i">ListViewItem</param>
    /// <param name="c">Column number</param>
    /// <returns>Text or empty string</returns>
    internal string ItemText( ListViewItem i, int c )
    {

      // ensure we have an item to work with here...
      if ( i != null )
      {

        // if column 0 return the text
        if ( c == 0 ) return i.Text;

        // not 0, ensure that the subitem is valid and exists
        if ( ( c < i.SubItems.Count ) && ( i.SubItems[ c ] != null ) )
          return i.SubItems[ c ].Text;

      }

      // not valid item/subitem return empty string
      return "";

    }


    /// <summary>
    /// Compare two strings and return a -/=/+ result.  This is
    /// used for all filter checks and column sorting.  The key
    /// is that we also use the DataType to correctly compare.
    /// </summary>
    /// <param name="s1">First string</param>
    /// <param name="s2">Second string</param>
    /// <param name="type">DataType for comparison</param>
    /// <returns>Less, Equal, Greater as -1,0,1</returns>
    internal int CompareData( string s1, string s2, LVFDataType type, bool size )
    {

      // perform the requested datatype comparison.  note that
      // we put the float and datetime in a try catch
      switch ( type )
      {

        // string comparison is easy.  if the size is true, this
        // is a filter comparison and only x characters count.
        case LVFDataType.String:
          if ( size && ( s1.Length > s2.Length ) )
            return s1.Substring( 0, s2.Length ).CompareTo( s2 );
          return s1.CompareTo( s2 );

        // float requires parsing the data
        case LVFDataType.Number:
          try { cmp_float1 = float.Parse( s1 ); }
          catch { cmp_float1 = float.MaxValue; };
          try { cmp_float2 = float.Parse( s2 ); }
          catch { cmp_float2 = float.MaxValue; };
          return cmp_float1.CompareTo( cmp_float2 );

        // date also requires a parse
        case LVFDataType.Date:
          try { cmp_datim1 = DateTime.Parse( s1 ); }
          catch { cmp_datim1 = DateTime.MaxValue; };
          try { cmp_datim2 = DateTime.Parse( s2 ); }
          catch { cmp_datim2 = DateTime.MaxValue; };
          return DateTime.Compare( cmp_datim1, cmp_datim2 );

        // by default the strings are equal
        default:
          return 0;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -