欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Winform在DataGridView中实现下拉筛选(保存筛选条件)

程序员文章站 2022-05-06 09:41:15
...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
using System.Collections;
using System.Reflection;

namespace MyClass
{

    public class DataGridViewAutoFilterColumnHeaderCell : DataGridViewColumnHeaderCell
    {

        /// <summary>
        /// The ListBox used for all drop-down lists.
        /// </summary>
        private FilterListBox dropDownListBox = new FilterListBox();

        /// <summary>
        /// A list of filters available for the owning column stored as
        /// formatted and unformatted string values.
        /// </summary>
        private System.Collections.Specialized.OrderedDictionary filters =
            new System.Collections.Specialized.OrderedDictionary();

        /// <summary>
        /// The drop-down list filter value currently in effect for the owning column.
        /// </summary>
        private String selectedFilterValue = String.Empty;

        /// <summary>
        /// The complete filter string currently in effect for the owning column.
        /// </summary>
        private String currentColumnFilter = String.Empty;

        /// <summary>
        /// Indicates whether the DataGridView is currently filtered by the owning column.
        /// </summary>
        private Boolean filtered;

        public static DataGridViewColumnHeaderCell oldHeader;

        /// <summary>
        /// Initializes a new instance of the DataGridViewColumnHeaderCell
        /// class and sets its property values to the property values of the
        /// specified DataGridViewColumnHeaderCell.
        /// </summary>
        /// <param name="oldHeaderCell">The DataGridViewColumnHeaderCell to copy property values from.</param>
        public DataGridViewAutoFilterColumnHeaderCell(DataGridViewColumnHeaderCell oldHeaderCell)
        {
            this.ContextMenuStrip = oldHeaderCell.ContextMenuStrip;
            this.ErrorText = oldHeaderCell.ErrorText;
            this.Tag = oldHeaderCell.Tag;
            this.ToolTipText = oldHeaderCell.ToolTipText;
            this.Value = oldHeaderCell.Value;
            this.ValueType = oldHeaderCell.ValueType;
            

            // Use HasStyle to avoid creating a new style object
            // when the Style property has not previously been set.
            if (oldHeaderCell.HasStyle)
            {
                this.Style = oldHeaderCell.Style;
            }

            // Copy this type's properties if the old cell is an auto-filter cell.
            // This enables the Clone method to reuse this constructor.
            DataGridViewAutoFilterColumnHeaderCell filterCell =
                oldHeaderCell as DataGridViewAutoFilterColumnHeaderCell;
            if (filterCell != null)
            {
                this.FilteringEnabled = filterCell.FilteringEnabled;
                this.AutomaticSortingEnabled = filterCell.AutomaticSortingEnabled;
                this.DropDownListBoxMaxLines = filterCell.DropDownListBoxMaxLines;
                this.currentDropDownButtonPaddingOffset =
                    filterCell.currentDropDownButtonPaddingOffset;
            }
        }

        /// <summary>
        /// Initializes a new instance of the DataGridViewColumnHeaderCell
        /// class.
        /// </summary>
        public DataGridViewAutoFilterColumnHeaderCell()
        {
            this.ContextMenuStrip = oldHeader.ContextMenuStrip;
            this.ErrorText = oldHeader.ErrorText;
            this.Tag = oldHeader.Tag;
            this.ToolTipText = oldHeader.ToolTipText;
            this.Value = oldHeader.Value;
            this.ValueType = oldHeader.ValueType;

            // Use HasStyle to avoid creating a new style object
            // when the Style property has not previously been set.
            if (oldHeader.HasStyle)
            {
                this.Style = oldHeader.Style;
            }

            // Copy this type's properties if the old cell is an auto-filter cell.
            // This enables the Clone method to reuse this constructor.
            DataGridViewAutoFilterColumnHeaderCell filterCell =
                oldHeader as DataGridViewAutoFilterColumnHeaderCell;
            if (filterCell != null)
            {
                this.FilteringEnabled = filterCell.FilteringEnabled;
                this.AutomaticSortingEnabled = filterCell.AutomaticSortingEnabled;
                this.DropDownListBoxMaxLines = filterCell.DropDownListBoxMaxLines;
                this.currentDropDownButtonPaddingOffset =
                    filterCell.currentDropDownButtonPaddingOffset;
            }
        }

        /// <summary>
        /// Creates an exact copy of this cell.
        /// </summary>
        /// <returns>An object that represents the cloned DataGridViewAutoFilterColumnHeaderCell.</returns>
        public override object Clone()
        {
            return new DataGridViewAutoFilterColumnHeaderCell(this);
        }

        /// <summary>
        /// Called when the value of the DataGridView property changes
        /// in order to perform initialization that requires access to the
        /// owning control and column.
        /// </summary>
        ///

        protected override void OnDataGridViewChanged()
        {
            // Continue only if there is a DataGridView.
            if (this.DataGridView == null)
            {
                return;
            }

            // Disable sorting and filtering for columns that can't make
            // effective use of them.
            if (OwningColumn != null)
            {
                if (OwningColumn is DataGridViewImageColumn ||
                (OwningColumn is DataGridViewButtonColumn &&
                ((DataGridViewButtonColumn)OwningColumn).UseColumnTextForButtonValue) ||
                (OwningColumn is DataGridViewLinkColumn &&
                ((DataGridViewLinkColumn)OwningColumn).UseColumnTextForLinkValue))
                {
                    AutomaticSortingEnabled = false;
                    FilteringEnabled = false;
                }

                // Ensure that the column SortMode property value is not Automatic.
                // This prevents sorting when the user clicks the drop-down button.
                if (OwningColumn.SortMode == DataGridViewColumnSortMode.Automatic)
                {
                    OwningColumn.SortMode = DataGridViewColumnSortMode.Programmatic;
                }
            }

            // Confirm that the data source meets requirements.
            VerifyDataSource();

            // Add handlers to DataGridView events.
            HandleDataGridViewEvents();

            // Initialize the drop-down button bounds so that any initial
            // column autosizing will accommodate the button width.
            SetDropDownButtonBounds();

            // Call the OnDataGridViewChanged method on the base class to
            // raise the DataGridViewChanged event.
            base.OnDataGridViewChanged();
        }

        /// <summary>
        /// Confirms that the data source, if it has been set, is a BindingSource.
        /// </summary>
        private void VerifyDataSource()
        {
            // Continue only if there is a DataGridView and its DataSource has been set.
            if (this.DataGridView == null || this.DataGridView.DataSource == null)
            {
                return;
            }

            // Throw an exception if the data source is not a BindingSource.
            BindingSource data = this.DataGridView.DataSource as BindingSource;
            if (data == null)
            {
                throw new NotSupportedException(
                    "The DataSource property of the containing DataGridView control " +
                    "must be set to a BindingSource.");
            }
        }

        #region DataGridView events: HandleDataGridViewEvents, DataGridView event handlers, ResetDropDown, ResetFilter

        /// <summary>
        /// Add handlers to various DataGridView events, primarily to invalidate
        /// the drop-down button bounds, hide the drop-down list, and reset
        /// cached filter values when changes in the DataGridView require it.
        /// </summary>
        private void HandleDataGridViewEvents()
        {
            this.DataGridView.Scroll += new ScrollEventHandler(DataGridView_Scroll);
            this.DataGridView.ColumnDisplayIndexChanged += new DataGridViewColumnEventHandler(DataGridView_ColumnDisplayIndexChanged);
            this.DataGridView.ColumnWidthChanged += new DataGridViewColumnEventHandler(DataGridView_ColumnWidthChanged);
            this.DataGridView.ColumnHeadersHeightChanged += new EventHandler(DataGridView_ColumnHeadersHeightChanged);
            this.DataGridView.SizeChanged += new EventHandler(DataGridView_SizeChanged);
            this.DataGridView.DataSourceChanged += new EventHandler(DataGridView_DataSourceChanged);
            this.DataGridView.DataBindingComplete += new DataGridViewBindingCompleteEventHandler(DataGridView_DataBindingComplete);

            // Add a handler for the ColumnSortModeChanged event to prevent the
            // column SortMode property from being inadvertently set to Automatic.
            this.DataGridView.ColumnSortModeChanged += new DataGridViewColumnEventHandler(DataGridView_ColumnSortModeChanged);
        }

        /// <summary>
        /// Invalidates the drop-down button bounds when the user scrolls horizontally.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A ScrollEventArgs that contains the event data.</param>
        private void DataGridView_Scroll(object sender, ScrollEventArgs e)
        {
            if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
            {
                ResetDropDown();
            }
        }

        /// <summary>
        /// Invalidates the drop-down button bounds when the column display index changes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataGridView_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e)
        {
            ResetDropDown();
        }

        /// <summary>
        /// Invalidates the drop-down button bounds when a column width changes
        /// in the DataGridView control. A width change in any column of the
        /// control has the potential to affect the drop-down button location,
        /// depending on the current horizontal scrolling position and whether
        /// the changed column is to the left or right of the current column.
        /// It is easier to invalidate the button in all cases.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A DataGridViewColumnEventArgs that contains the event data.</param>
        private void DataGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e)
        {
            ResetDropDown();
        }

        /// <summary>
        /// Invalidates the drop-down button bounds when the height of the column headers changes.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        private void DataGridView_ColumnHeadersHeightChanged(object sender, EventArgs e)
        {
            ResetDropDown();
        }

        /// <summary>
        /// Invalidates the drop-down button bounds when the size of the DataGridView changes.
        /// This prevents a painting issue that occurs when the right edge of the control moves
        /// to the right and the control contents have previously been scrolled to the right.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        private void DataGridView_SizeChanged(object sender, EventArgs e)
        {
            ResetDropDown();
        }

        /// <summary>
        /// Invalidates the drop-down button bounds, hides the drop-down
        /// filter list, if it is showing, and resets the cached filter values
        /// if the filter has been removed.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A DataGridViewBindingCompleteEventArgs that contains the event data.</param>
        private void DataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            if (e.ListChangedType == ListChangedType.Reset)
            {
                ResetDropDown();
                ResetFilter();
            }
        }

        /// <summary>
        /// Verifies that the data source meets requirements, invalidates the
        /// drop-down button bounds, hides the drop-down filter list if it is
        /// showing, and resets the cached filter values if the filter has been removed.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        private void DataGridView_DataSourceChanged(object sender, EventArgs e)
        {
            VerifyDataSource();
            ResetDropDown();
            ResetFilter();
        }

        /// <summary>
        /// Invalidates the drop-down button bounds and hides the filter
        /// list if it is showing.
        /// </summary>
        private void ResetDropDown()
        {
            InvalidateDropDownButtonBounds();
            if (dropDownListBoxShowing)
            {
                HideDropDownList();
            }
        }

        /// <summary>
        /// Resets the cached filter values if the filter has been removed.
        /// </summary>
        private void ResetFilter()
        {
            if (this.DataGridView == null) return;
            BindingSource source = this.DataGridView.DataSource as BindingSource;
            if (source == null || String.IsNullOrEmpty(source.Filter))
            {
                filtered = false;
                selectedFilterValue = "(All)";
                currentColumnFilter = String.Empty;
            }
        }

        /// <summary>
        /// Throws an exception when the column sort mode is changed to Automatic.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A DataGridViewColumnEventArgs that contains the event data.</param>
        private void DataGridView_ColumnSortModeChanged(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column == OwningColumn &&
                e.Column.SortMode == DataGridViewColumnSortMode.Automatic)
            {
                throw new InvalidOperationException(
                    "A SortMode value of Automatic is incompatible with " +
                    "the DataGridViewAutoFilterColumnHeaderCell type. " +
                    "Use the AutomaticSortingEnabled property instead.");
            }
        }

        #endregion DataGridView events

        /// <summary>
        /// Paints the column header cell, including the drop-down button.
        /// </summary>
        /// <param name="graphics">The Graphics used to paint the DataGridViewCell.</param>
        /// <param name="clipBounds">A Rectangle that represents the area of the DataGridView that needs to be repainted.</param>
        /// <param name="cellBounds">A Rectangle that contains the bounds of the DataGridViewCell that is being painted.</param>
        /// <param name="rowIndex">The row index of the cell that is being painted.</param>
        /// <param name="cellState">A bitwise combination of DataGridViewElementStates values that specifies the state of the cell.</param>
        /// <param name="value">The data of the DataGridViewCell that is being painted.</param>
        /// <param name="formattedValue">The formatted data of the DataGridViewCell that is being painted.</param>
        /// <param name="errorText">An error message that is associated with the cell.</param>
        /// <param name="cellStyle">A DataGridViewCellStyle that contains formatting and style information about the cell.</param>
        /// <param name="advancedBorderStyle">A DataGridViewAdvancedBorderStyle that contains border styles for the cell that is being painted.</param>
        /// <param name="paintParts">A bitwise combination of the DataGridViewPaintParts values that specifies which parts of the cell need to be painted.</param>
        protected override void Paint(
            Graphics graphics, Rectangle clipBounds, Rectangle cellBounds,
            int rowIndex, DataGridViewElementStates cellState,
            object value, object formattedValue, string errorText,
            DataGridViewCellStyle cellStyle,
            DataGridViewAdvancedBorderStyle advancedBorderStyle,
            DataGridViewPaintParts paintParts)
        {
            // Use the base method to paint the default appearance.
            base.Paint(graphics, clipBounds, cellBounds, rowIndex,
                cellState, value, formattedValue,
                errorText, cellStyle, advancedBorderStyle, paintParts);

            // Continue only if filtering is enabled and ContentBackground is
            // part of the paint request.
            if (!FilteringEnabled ||
                (paintParts & DataGridViewPaintParts.ContentBackground) == 0)
            {
                return;
            }

            // Retrieve the current button bounds.
            Rectangle buttonBounds = DropDownButtonBounds;

            // Continue only if the buttonBounds is big enough to draw.
            if (buttonBounds.Width < 1 || buttonBounds.Height < 1) return;

            // Paint the button manually or using visual styles if visual styles
            // are enabled, using the correct state depending on whether the
            // filter list is showing and whether there is a filter in effect
            // for the current column.
            if (Application.RenderWithVisualStyles)
            {
                ComboBoxState state = ComboBoxState.Normal;

                if (dropDownListBoxShowing)
                {
                    state = ComboBoxState.Pressed;
                }
                else if (filtered)
                {
                    state = ComboBoxState.Hot;
                }
                ComboBoxRenderer.DrawDropDownButton(
                    graphics, buttonBounds, state);
            }
            else
            {
                // Determine the pressed state in order to paint the button
                // correctly and to offset the down arrow.
                Int32 pressedOffset = 0;
                PushButtonState state = PushButtonState.Normal;
                if (dropDownListBoxShowing)
                {
                    state = PushButtonState.Pressed;
                    pressedOffset = 1;
                }
                ButtonRenderer.DrawButton(graphics, buttonBounds, state);

                // If there is a filter in effect for the column, paint the
                // down arrow as an unfilled triangle. If there is no filter
                // in effect, paint the down arrow as a filled triangle.
                if (filtered)
                {
                    graphics.DrawPolygon(SystemPens.ControlText, new Point[] {
                        new Point(
                            buttonBounds.Width / 2 +
                                buttonBounds.Left - 1 + pressedOffset,
                            buttonBounds.Height * 3 / 4 +
                                buttonBounds.Top - 1 + pressedOffset),
                        new Point(
                            buttonBounds.Width / 4 +
                                buttonBounds.Left + pressedOffset,
                            buttonBounds.Height / 2 +
                                buttonBounds.Top - 1 + pressedOffset),
                        new Point(
                            buttonBounds.Width * 3 / 4 +
                                buttonBounds.Left - 1 + pressedOffset,
                            buttonBounds.Height / 2 +
                                buttonBounds.Top - 1 + pressedOffset)
                    });
                }
                else
                {
                    graphics.FillPolygon(SystemBrushes.ControlText, new Point[] {
                        new Point(
                            buttonBounds.Width / 2 +
                                buttonBounds.Left - 1 + pressedOffset,
                            buttonBounds.Height * 3 / 4 +
                                buttonBounds.Top - 1 + pressedOffset),
                        new Point(
                            buttonBounds.Width / 4 +
                                buttonBounds.Left + pressedOffset,
                            buttonBounds.Height / 2 +
                                buttonBounds.Top - 1 + pressedOffset),
                        new Point(
                            buttonBounds.Width * 3 / 4 +
                                buttonBounds.Left - 1 + pressedOffset,
                            buttonBounds.Height / 2 +
                                buttonBounds.Top - 1 + pressedOffset)
                    });
                }
            }

        }

        private static string buttonName = string.Empty;

        /// <summary>
        /// Handles mouse clicks to the header cell, displaying the
        /// drop-down list or sorting the owning column as appropriate.
        /// </summary>
        /// <param name="e">A DataGridViewCellMouseEventArgs that contains the event data.</param>
        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            Debug.Assert(this.DataGridView != null, "DataGridView is null");
            buttonName = this.DataGridView.Columns[e.ColumnIndex].HeaderText;
            // Continue only if the user did not click the drop-down button
            // while the drop-down list was displayed. This prevents the
            // drop-down list from being redisplayed after being hidden in
            // the LostFocus event handler.
            if (lostFocusOnDropDownButtonClick)
            {
                lostFocusOnDropDownButtonClick = false;
                return;
            }

            // Retrieve the current size and location of the header cell,
            // excluding any portion that is scrolled off screen.
            Rectangle cellBounds = this.DataGridView
                .GetCellDisplayRectangle(e.ColumnIndex, -1, false);

            // Continue only if the column is not manually resizable or the
            // mouse coordinates are not within the column resize zone.
            if (this.OwningColumn.Resizable == DataGridViewTriState.True &&
                ((this.DataGridView.RightToLeft == RightToLeft.No &&
                cellBounds.Width - e.X < 6) || e.X < 6))
            {
                return;
            }

            // Unless RightToLeft is enabled, store the width of the portion
            // that is scrolled off screen.
            Int32 scrollingOffset = 0;
            if (this.DataGridView.RightToLeft == RightToLeft.No &&
                this.DataGridView.FirstDisplayedScrollingColumnIndex ==
                this.ColumnIndex)
            {
                scrollingOffset =
                    this.DataGridView.FirstDisplayedScrollingColumnHiddenWidth;
            }

            // Show the drop-down list if filtering is enabled and the mouse click occurred
            // within the drop-down button bounds. Otherwise, if sorting is enabled and the
            // click occurred outside the drop-down button bounds, sort by the owning column.
            // The mouse coordinates are relative to the cell bounds, so the cell location
            // and the scrolling offset are needed to determine the client coordinates.
            if (FilteringEnabled &&
                DropDownButtonBounds.Contains(
                e.X + cellBounds.Left - scrollingOffset, e.Y + cellBounds.Top))
            {
                // If the current cell is in edit mode, commit the edit.
                if (this.DataGridView.IsCurrentCellInEditMode)
                {
                    // Commit and end the cell edit.
                    this.DataGridView.EndEdit();

                    // Commit any change to the underlying data source.
                    BindingSource source =
                        this.DataGridView.DataSource as BindingSource;
                    if (source != null)
                    {
                        source.EndEdit();
                    }
                }
                ShowDropDownList();
            }
            else if (AutomaticSortingEnabled &&
                this.DataGridView.SelectionMode !=
                DataGridViewSelectionMode.ColumnHeaderSelect)
            {
                SortByColumn();
            }

            base.OnMouseDown(e);
        }

        /// <summary>
        /// Sorts the DataGridView by the current column if AutomaticSortingEnabled is true.
        /// </summary>
        private void SortByColumn()
        {
            Debug.Assert(this.DataGridView != null && OwningColumn != null, "DataGridView or OwningColumn is null");

            // Continue only if the data source supports sorting.
            IBindingList sortList = this.DataGridView.DataSource as IBindingList;
            if (sortList == null ||
                !sortList.SupportsSorting ||
                !AutomaticSortingEnabled)
            {
                return;
            }

            // Determine the sort direction and sort by the owning column.
            ListSortDirection direction = ListSortDirection.Ascending;
            if (this.DataGridView.SortedColumn == OwningColumn &&
                this.DataGridView.SortOrder == SortOrder.Ascending)
            {
                direction = ListSortDirection.Descending;
            }
            this.DataGridView.Sort(OwningColumn, direction);
        }

        #region drop-down list: Show/HideDropDownListBox, SetDropDownListBoxBounds, DropDownListBoxMaxHeightInternal

        /// <summary>
        /// Indicates whether dropDownListBox is currently displayed
        /// for this header cell.
        /// </summary>
        private bool dropDownListBoxShowing;

        /// <summary>
        /// Displays the drop-down filter list.
        /// </summary>
        public void ShowDropDownList()
        {
            Debug.Assert(this.DataGridView != null, "DataGridView is null");

            // Ensure that the current row is not the row for new records.
            // This prevents the new row from affecting the filter list and also
            // prevents the new row from being added when the filter changes.
            if (this.DataGridView.CurrentRow != null &&
                this.DataGridView.CurrentRow.IsNewRow)
            {
                this.DataGridView.CurrentCell = null;
            }

            // Populate the filters dictionary, then copy the filter values
            // from the filters.Keys collection into the ListBox.Items collection,
            // selecting the current filter if there is one in effect.
            PopulateFilters();

            String[] filterArray = new String[filters.Count];
            filters.Keys.CopyTo(filterArray, 0);
            dropDownListBox.Name = this.DataGridView.Columns[this.ColumnIndex].HeaderText;
            dropDownListBox.Items.Clear();
            dropDownListBox.Items.AddRange(filterArray);
            dropDownListBox.SelectedItem = selectedFilterValue;

            if(dropDownListBox.SelectedItem == null)
            {
                foreach (var item in DataGridViewFunction.ListCondition)
                {
                    if(item.Key == dropDownListBox.Name)
                    {
                        if (dropDownListBox.Items.Contains(item.Value))
                        {
                            dropDownListBox.SelectedItem = item.Value;
                        }
                    }
                }
            }

            // Add handlers to dropDownListBox events.
            HandleDropDownListBoxEvents();

            // Set the size and location of dropDownListBox, then display it.
            SetDropDownListBoxBounds();
            dropDownListBox.Visible = true;
            dropDownListBoxShowing = true;


            foreach (Control item in this.DataGridView.Controls)
            {
                if (item.Name == this.DataGridView.Columns[this.ColumnIndex].HeaderText)
                {
                    this.DataGridView.Controls.Remove(item);
                }
            }
            Debug.Assert(dropDownListBox.Parent == null,
                "ShowDropDownListBox has been called multiple times before HideDropDownListBox");

            // Add dropDownListBox to the DataGridView.
            this.DataGridView.Controls.Add(dropDownListBox);

            // Set the input focus to dropDownListBox.
            dropDownListBox.Focus();

            // Invalidate the cell so that the drop-down button will repaint
            // in the pressed state.
            this.DataGridView.InvalidateCell(this);
        }

        /// <summary>
        /// Hides the drop-down filter list.
        /// </summary>
        public void HideDropDownList()
        {
            if (DataGridView != null)
            {
                Debug.Assert(this.DataGridView != null, "DataGridView is null");

                // Hide dropDownListBox, remove handlers from its events, and remove
                // it from the DataGridView control.
                dropDownListBoxShowing = false;
                dropDownListBox.Visible = false;
                UnhandleDropDownListBoxEvents();
                this.DataGridView.Controls.Remove(dropDownListBox);

                // Invalidate the cell so that the drop-down button will repaint
                // in the unpressed state.
                this.DataGridView.InvalidateCell(this);
            }
        }

        /// <summary>
        /// Sets the dropDownListBox size and position based on the formatted
        /// values in the filters dictionary and the position of the drop-down
        /// button. Called only by ShowDropDownListBox.
        /// </summary>
        private void SetDropDownListBoxBounds()
        {
            Debug.Assert(filters.Count > 0, "filters.Count <= 0");

            // Declare variables that will be used in the calculation,
            // initializing dropDownListBoxHeight to account for the
            // ListBox borders.
            Int32 dropDownListBoxHeight = 2;
            Int32 currentWidth = 0;
            Int32 dropDownListBoxWidth = 0;
            Int32 dropDownListBoxLeft = 0;

            // For each formatted value in the filters dictionary Keys collection,
            // add its height to dropDownListBoxHeight and, if it is wider than
            // all previous values, set dropDownListBoxWidth to its width.
            using (Graphics graphics = dropDownListBox.CreateGraphics())
            {
                foreach (String filter in filters.Keys)
                {
                    SizeF stringSizeF = graphics.MeasureString(
                        filter, dropDownListBox.Font);
                    dropDownListBoxHeight += (Int32)stringSizeF.Height;
                    currentWidth = (Int32)stringSizeF.Width;
                    if (dropDownListBoxWidth < currentWidth)
                    {
                        dropDownListBoxWidth = currentWidth;
                    }
                }
            }

            // Increase the width to allow for horizontal margins and borders.
            dropDownListBoxWidth += 6;

            // Constrain the dropDownListBox height to the
            // DropDownListBoxMaxHeightInternal value, which is based on
            // the DropDownListBoxMaxLines property value but constrained by
            // the maximum height available in the DataGridView control.
            if (dropDownListBoxHeight > DropDownListBoxMaxHeightInternal)
            {
                dropDownListBoxHeight = DropDownListBoxMaxHeightInternal;

                // If the preferred height is greater than the available height,
                // adjust the width to accommodate the vertical scroll bar.
                dropDownListBoxWidth += SystemInformation.VerticalScrollBarWidth;
            }

            // Calculate the ideal location of the left edge of dropDownListBox
            // based on the location of the drop-down button and taking the
            // RightToLeft property value into consideration.
            if (this.DataGridView.RightToLeft == RightToLeft.No)
            {
                dropDownListBoxLeft = DropDownButtonBounds.Right -
                    dropDownListBoxWidth + 1;
            }
            else
            {
                dropDownListBoxLeft = DropDownButtonBounds.Left - 1;
            }

            // Determine the left and right edges of the available horizontal
            // width of the DataGridView control.
            Int32 clientLeft = 1;
            Int32 clientRight = this.DataGridView.ClientRectangle.Right;
            if (this.DataGridView.DisplayedRowCount(false) <
                this.DataGridView.RowCount)
            {
                if (this.DataGridView.RightToLeft == RightToLeft.Yes)
                {
                    clientLeft += SystemInformation.VerticalScrollBarWidth;
                }
                else
                {
                    clientRight -= SystemInformation.VerticalScrollBarWidth;
                }
            }

            // Adjust the dropDownListBox location and/or width if it would
            // otherwise overlap the left or right edge of the DataGridView.
            if (dropDownListBoxLeft < clientLeft)
            {
                dropDownListBoxLeft = clientLeft;
            }
            Int32 dropDownListBoxRight =
                dropDownListBoxLeft + dropDownListBoxWidth + 1;
            if (dropDownListBoxRight > clientRight)
            {
                if (dropDownListBoxLeft == clientLeft)
                {
                    dropDownListBoxWidth -=
                        dropDownListBoxRight - clientRight;
                }
                else
                {
                    dropDownListBoxLeft -=
                        dropDownListBoxRight - clientRight;
                    if (dropDownListBoxLeft < clientLeft)
                    {
                        dropDownListBoxWidth -= clientLeft - dropDownListBoxLeft;
                        dropDownListBoxLeft = clientLeft;
                    }
                }
            }

            // Set the ListBox.Bounds property using the calculated values.
            dropDownListBox.Bounds = new Rectangle(dropDownListBoxLeft,
                DropDownButtonBounds.Bottom, // top of drop-down list box
                dropDownListBoxWidth, dropDownListBoxHeight);
        }

        /// <summary>
        /// Gets the actual maximum height of the drop-down list, in pixels.
        /// The maximum height is calculated from the DropDownListBoxMaxLines
        /// property value, but is limited to the available height of the
        /// DataGridView control.
        /// </summary>
        protected Int32 DropDownListBoxMaxHeightInternal
        {
            get
            {
                // Calculate the height of the available client area
                // in the DataGridView control, taking the horizontal
                // scroll bar into consideration and leaving room
                // for the ListBox bottom border.
                Int32 dataGridViewMaxHeight = this.DataGridView.Height -
                    this.DataGridView.ColumnHeadersHeight - 1;
                if (this.DataGridView.DisplayedColumnCount(false) <
                    this.DataGridView.ColumnCount)
                {
                    dataGridViewMaxHeight -=
                        SystemInformation.HorizontalScrollBarHeight;
                }

                // Calculate the height of the list box, using the combined
                // height of all items plus 2 for the top and bottom border.
                Int32 listMaxHeight = dropDownListBoxMaxLinesValue * dropDownListBox.ItemHeight + 2;

                // Return the smaller of the two values.
                if (listMaxHeight < dataGridViewMaxHeight)
                {
                    return listMaxHeight;
                }
                else
                {
                    return dataGridViewMaxHeight;
                }
            }
        }

        #endregion drop-down list

        #region ListBox events: HandleDropDownListBoxEvents, UnhandleDropDownListBoxEvents, ListBox event handlers

        /// <summary>
        /// Adds handlers to ListBox events for handling mouse
        /// and keyboard input.
        /// </summary>
        private void HandleDropDownListBoxEvents()
        {
            dropDownListBox.MouseClick += new MouseEventHandler(DropDownListBox_MouseClick);
            dropDownListBox.LostFocus += new EventHandler(DropDownListBox_LostFocus);
            dropDownListBox.KeyDown += new KeyEventHandler(DropDownListBox_KeyDown);
        }

        /// <summary>
        /// Removes the ListBox event handlers.
        /// </summary>
        private void UnhandleDropDownListBoxEvents()
        {
            dropDownListBox.MouseClick -= new MouseEventHandler(DropDownListBox_MouseClick);
            dropDownListBox.LostFocus -= new EventHandler(DropDownListBox_LostFocus);
            dropDownListBox.KeyDown -= new KeyEventHandler(DropDownListBox_KeyDown);
        }

        /// <summary>
        /// Adjusts the filter in response to a user selection from the drop-down list.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A MouseEventArgs that contains the event data.</param>
        private void DropDownListBox_MouseClick(object sender, MouseEventArgs e)
        {

            Debug.Assert(this.DataGridView != null, "DataGridView is null");
            ListBox listBox = sender as ListBox;
            string ss = listBox.SelectedItem.ToString();
            // Continue only if the mouse click was in the content area
            // and not on the scroll bar.
            if (!dropDownListBox.DisplayRectangle.Contains(e.X, e.Y))
            {
                return;
            }
            string name = DataGridViewAutoFilterColumnHeaderCell.buttonName;
            DataGridViewFunction.AddCondition(name, ss);
            if (ss == "(All)")
            {
                HideDropDownList();
                return;
            }
            UpdateFilter();
            HideDropDownList();
            this.DataGridView.ClearSelection();
        }

        /// <summary>
        /// Indicates whether the drop-down list lost focus because the
        /// user clicked the drop-down button.
        /// </summary>
        private Boolean lostFocusOnDropDownButtonClick;

        /// <summary>
        /// Hides the drop-down list when it loses focus.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">An EventArgs that contains the event data.</param>
        private void DropDownListBox_LostFocus(object sender, EventArgs e)
        {
            // If the focus was lost because the user clicked the drop-down
            // button, store a value that prevents the subsequent OnMouseDown
            // call from displaying the drop-down list again.
            if (DropDownButtonBounds.Contains(
                this.DataGridView.PointToClient(new Point(
                Control.MousePosition.X, Control.MousePosition.Y))))
            {
                lostFocusOnDropDownButtonClick = true;
            }
            HideDropDownList();
            this.DataGridView.ClearSelection();
        }

        /// <summary>
        /// Handles the ENTER and ESC keys.
        /// </summary>
        /// <param name="sender">The object that raised the event.</param>
        /// <param name="e">A KeyEventArgs that contains the event data.</param>
        void DropDownListBox_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {
                case Keys.Enter:
                    UpdateFilter();
                    HideDropDownList();
                    break;
                case Keys.Escape:
                    HideDropDownList();
                    break;
            }
            this.DataGridView.ClearSelection();
        }

        #endregion ListBox events

        #region filtering: PopulateFilters, FilterWithoutCurrentColumn, UpdateFilter, RemoveFilter, AvoidNewRowWhenFiltering, GetFilterStatus

        /// <summary>
        /// Populates the filters dictionary with formatted and unformatted string
        /// representations of each unique value in the column, accounting for all
        /// filters except the current column's. Also adds special filter options.
        /// </summary>
        private void PopulateFilters()
        {
            // Continue only if there is a DataGridView.
            if (this.DataGridView == null)
            {
                return;
            }

            // Cast the data source to a BindingSource.
            BindingSource data = this.DataGridView.DataSource as BindingSource;

            Debug.Assert(data != null && data.SupportsFiltering && OwningColumn != null,
                "DataSource is not a BindingSource, or does not support filtering, or OwningColumn is null");

            // Prevent the data source from notifying the DataGridView of changes.
            data.RaiseListChangedEvents = false;

            // Cache the current BindingSource.Filter value and then change
            // the Filter property to temporarily remove any filter for the
            // current column.
            String oldFilter = data.Filter;
            data.Filter = FilterWithoutCurrentColumn(oldFilter);

            // Reset the filters dictionary and initialize some flags
            // to track whether special filter options are needed.
            filters.Clear();
            Boolean containsBlanks = false;
            Boolean containsNonBlanks = false;

            // Initialize an ArrayList to store the values in their original
            // types. This enables the values to be sorted appropriately.
            ArrayList list = new ArrayList(data.Count);

            // Retrieve each value and add it to the ArrayList if it isn't
            // already present.
            foreach (Object item in data)
            {
                Object value = null;

                // Use the ICustomTypeDescriptor interface to retrieve properties
                // if it is available; otherwise, use reflection. The
                // ICustomTypeDescriptor interface is useful to customize
                // which values are exposed as properties. For example, the
                // DataRowView class implements ICustomTypeDescriptor to expose
                // cell values as property values.
                //
                // Iterate through the property names to find a case-insensitive
                // match with the DataGridViewColumn.DataPropertyName value.
                // This is necessary because DataPropertyName is case-
                // insensitive, but the GetProperties and GetProperty methods
                // used below are case-sensitive.
                ICustomTypeDescriptor ictd = item as ICustomTypeDescriptor;
                if (ictd != null)
                {
                    PropertyDescriptorCollection properties = ictd.GetProperties();
                    foreach (PropertyDescriptor property in properties)
                    {
                        if (String.Compare(this.OwningColumn.DataPropertyName,
                            property.Name, true /*case insensitive*/,
                            System.Globalization.CultureInfo.InvariantCulture) == 0)
                        {
                            value = property.GetValue(item);
                            break;
                        }
                    }
                }
                else
                {
                    PropertyInfo[] properties = item.GetType().GetProperties(
                        BindingFlags.Public | BindingFlags.Instance);
                    foreach (PropertyInfo property in properties)
                    {
                        if (String.Compare(this.OwningColumn.DataPropertyName,
                            property.Name, true /*case insensitive*/,
                            System.Globalization.CultureInfo.InvariantCulture) == 0)
                        {
                            value = property.GetValue(item, null /*property index*/);
                            break;
                        }
                    }
                }

                // Skip empty values, but note that they are present.
                if (value == null || value == DBNull.Value)
                {
                    containsBlanks = true;
                    continue;
                }

                // Add values to the ArrayList if they are not already there.
                if (!list.Contains(value))
                {
                    list.Add(value);
                }
            }

            // Sort the ArrayList. The default Sort method uses the IComparable
            // implementation of the stored values so that string, numeric, and
            // date values will all be sorted correctly.
            list.Sort();

            // Convert each value in the ArrayList to its formatted representation
            // and store both the formatted and unformatted string representations
            // in the filters dictionary.
            foreach (Object value in list)
            {
                // Use the cell's GetFormattedValue method with the column's
                // InheritedStyle property so that the dropDownListBox format
                // will match the display format used for the column's cells.
                String formattedValue = null;
                DataGridViewCellStyle style = OwningColumn.InheritedStyle;
                formattedValue = (String)GetFormattedValue(value, -1, ref style,
                    null, null, DataGridViewDataErrorContexts.Formatting);

                if (String.IsNullOrEmpty(formattedValue))
                {
                    // Skip empty values, but note that they are present.
                    containsBlanks = true;
                }
                else if (!filters.Contains(formattedValue))
                {
                    // Note whether non-empty values are present.
                    containsNonBlanks = true;

                    // For all non-empty values, add the formatted and
                    // unformatted string representations to the filters
                    // dictionary.
                    filters.Add(formattedValue, value.ToString());
                }
            }

            // Restore the filter to the cached filter string and
            // re-enable data source change notifications.
            if (oldFilter != null) data.Filter = oldFilter;
            data.RaiseListChangedEvents = true;

            // Add special filter options to the filters dictionary
            // along with null values, since unformatted representations
            // are not needed.
            filters.Insert(0, "(All)", null);
            if (containsBlanks && containsNonBlanks)
            {
                filters.Add("(Blanks)", null);
                filters.Add("(NonBlanks)", null);
            }
        }

        /// <summary>
        /// Returns a copy of the specified filter string after removing the part that filters the current column, if present.
        /// </summary>
        /// <param name="filter">The filter string to parse.</param>
        /// <returns>A copy of the specified filter string without the current column's filter.</returns>
        private String FilterWithoutCurrentColumn(String filter)
        {
            // If there is no filter in effect, return String.Empty.
            if (String.IsNullOrEmpty(filter))
            {
                return String.Empty;
            }

            // If the column is not filtered, return the filter string unchanged.
            if (!filtered)
            {
                return filter;
            }

            if (filter.IndexOf(currentColumnFilter) > 0)
            {
                // If the current column filter is not the first filter, return
                // the specified filter value without the current column filter
                // and without the preceding " AND ".
                return filter.Replace(
                    " AND " + currentColumnFilter, String.Empty);
            }
            else
            {
                if (filter.Length > currentColumnFilter.Length)
                {
                    // If the current column filter is the first of multiple
                    // filters, return the specified filter value without the
                    // current column filter and without the subsequent " AND ".
                    return filter.Replace(
                        currentColumnFilter + " AND ", String.Empty);
                }
                else
                {
                    // If the current column filter is the only filter,
                    // return the empty string.
                    return String.Empty;
                }
            }
        }

        /// <summary>
        /// Updates the BindingSource.Filter value based on a user selection
        /// from the drop-down filter list.
        /// </summary>
        public void UpdateFilter()
        {
            // Continue only if the selection has changed.
            if(dropDownListBox.SelectedItem == null)
            {
                return;
            }

            if (dropDownListBox.SelectedItem.ToString().Equals(selectedFilterValue))
            {
                return;
            }

            // Store the new selection value.
            selectedFilterValue = dropDownListBox.SelectedItem.ToString();

            // Cast the data source to an IBindingListView.
            IBindingListView data =
                this.DataGridView.DataSource as IBindingListView;

            Debug.Assert(data != null && data.SupportsFiltering,
                "DataSource is not an IBindingListView or does not support filtering");

            // If the user selection is (All), remove any filter currently
            // in effect for the column.
            if (selectedFilterValue.Equals("(All)"))
            {
                data.Filter = FilterWithoutCurrentColumn(data.Filter);
                filtered = false;
                currentColumnFilter = String.Empty;
                return;
            }

            // Declare a variable to store the filter string for this column.
            String newColumnFilter = null;

            // Store the column name in a form acceptable to the Filter property,
            // using a backslash to escape any closing square brackets.
            String columnProperty =
                OwningColumn.DataPropertyName.Replace("]", @"\]");

            // Determine the column filter string based on the user selection.
            // For (Blanks) and (NonBlanks), the filter string determines whether
            // the column value is null or an empty string. Otherwise, the filter
            // string determines whether the column value is the selected value.
            switch (selectedFilterValue)
            {
                case "(Blanks)":
                    newColumnFilter = String.Format(
                        "LEN(ISNULL(CONVERT([{0}],'System.String'),''))=0",
                        columnProperty);
                    break;
                case "(NonBlanks)":
                    newColumnFilter = String.Format(
                        "LEN(ISNULL(CONVERT([{0}],'System.String'),''))>0",
                        columnProperty);
                    break;
                default:
                    newColumnFilter = String.Format("[{0}]='{1}'",
                        columnProperty,
                        ((String)filters[selectedFilterValue])
                        .Replace("'", "''"));
                    break;
            }

            // Determine the new filter string by removing the previous column
            // filter string from the BindingSource.Filter value, then appending
            // the new column filter string, using " AND " as appropriate.
            String newFilter = FilterWithoutCurrentColumn(data.Filter);
            if (String.IsNullOrEmpty(newFilter))
            {
                newFilter += newColumnFilter;
            }
            else
            {
                newFilter += " AND " + newColumnFilter;
            }


            // Set the filter to the new value.
            try
            {
                data.Filter = newFilter;
            }
            catch (InvalidExpressionException ex)
            {
                throw new NotSupportedException(
                    "Invalid expression: " + newFilter, ex);
            }

            // Indicate that the column is currently filtered
            // and store the new column filter for use by subsequent
            // calls to the FilterWithoutCurrentColumn method.
            filtered = true;
            currentColumnFilter = newColumnFilter;
        }

        /// <summary>
        /// Removes the filter from the BindingSource bound to the specified DataGridView.
        /// </summary>
        /// <param name="dataGridView">The DataGridView bound to the BindingSource to unfilter.</param>
        public static void RemoveFilter(DataGridView dataGridView)
        {
            if (dataGridView == null)
            {
                throw new ArgumentNullException("dataGridView");
            }

            // Cast the data source to a BindingSource.
            BindingSource data = dataGridView.DataSource as BindingSource;

            // Confirm that the data source is a BindingSource that
            // supports filtering.
            if (data == null ||
                data.DataSource == null ||
                !data.SupportsFiltering)
            {
                throw new ArgumentException("The DataSource property of the " +
                    "specified DataGridView is not set to a BindingSource " +
                    "with a SupportsFiltering property value of true.");
            }

            // Ensure that the current row is not the row for new records.
            // This prevents the new row from being added when the filter changes.
            if (dataGridView.CurrentRow != null && dataGridView.CurrentRow.IsNewRow)
            {
                dataGridView.CurrentCell = null;
            }

            // Remove the filter.
            data.Filter = null;
        }

        /// <summary>
        /// Gets a status string for the specified DataGridView indicating the
        /// number of visible rows in the bound, filtered BindingSource, or
        /// String.Empty if all rows are currently visible.
        /// </summary>
        /// <param name="dataGridView">The DataGridView bound to the
        /// BindingSource to return the filter status for.</param>
        /// <returns>A string in the format "x of y records found" where x is
        /// the number of rows currently displayed and y is the number of rows
        /// available, or String.Empty if all rows are currently displayed.</returns>
        public static String GetFilterStatus(DataGridView dataGridView)
        {
            // Continue only if the specified value is valid.
            if (dataGridView == null)
            {
                throw new ArgumentNullException("dataGridView");
            }

            // Cast the data source to a BindingSource.
            BindingSource data = dataGridView.DataSource as BindingSource;

            // Return String.Empty if there is no appropriate data source or
            // there is no filter in effect.
            if (String.IsNullOrEmpty(data.Filter) ||
                data == null ||
                data.DataSource == null ||
                !data.SupportsFiltering)
            {
                return String.Empty;
            }

            // Retrieve the filtered row count.
            Int32 currentRowCount = data.Count;

            // Retrieve the unfiltered row count by
            // temporarily unfiltering the data.
            data.RaiseListChangedEvents = false;
            String oldFilter = data.Filter;
            data.Filter = null;
            Int32 unfilteredRowCount = data.Count;
            data.Filter = oldFilter;
            data.RaiseListChangedEvents = true;

            Debug.Assert(currentRowCount <= unfilteredRowCount,
                "current count is greater than unfiltered count");

            // Return String.Empty if the filtered and unfiltered counts
            // are the same, otherwise, return the status string.
            if (currentRowCount == unfilteredRowCount)
            {
                return String.Empty;
            }
            return String.Format("{0}",
                currentRowCount, unfilteredRowCount);
            //of {1} records found
        }

        #endregion filtering

        #region button bounds: DropDownButtonBounds, InvalidateDropDownButtonBounds, SetDropDownButtonBounds, AdjustPadding

        /// <summary>
        /// The bounds of the drop-down button, or Rectangle.Empty if filtering
        /// is disabled or the button bounds need to be recalculated.
        /// </summary>
        private Rectangle dropDownButtonBoundsValue = Rectangle.Empty;

        /// <summary>
        /// The bounds of the drop-down button, or Rectangle.Empty if filtering
        /// is disabled. Recalculates the button bounds if filtering is enabled
        /// and the bounds are empty.
        /// </summary>
        protected Rectangle DropDownButtonBounds
        {
            get
            {
                if (!FilteringEnabled)
                {
                    return Rectangle.Empty;
                }
                if (dropDownButtonBoundsValue == Rectangle.Empty)
                {
                    SetDropDownButtonBounds();
                }
                return dropDownButtonBoundsValue;
            }
        }

        /// <summary>
        /// Sets dropDownButtonBoundsValue to Rectangle.Empty if it isn't already empty.
        /// This indicates that the button bounds should be recalculated.
        /// </summary>
        private void InvalidateDropDownButtonBounds()
        {
            if (!dropDownButtonBoundsValue.IsEmpty)
            {
                dropDownButtonBoundsValue = Rectangle.Empty;
            }
        }

        /// <summary>
        /// Sets the position and size of dropDownButtonBoundsValue based on the current
        /// cell bounds and the preferred cell height for a single line of header text.
        /// </summary>
        private void SetDropDownButtonBounds()
        {
            // Retrieve the cell display rectangle, which is used to
            // set the position of the drop-down button.
            Rectangle cellBounds =
                this.DataGridView.GetCellDisplayRectangle(
                this.ColumnIndex, -1, false);

            // Initialize a variable to store the button edge length,
            // setting its initial value based on the font height.
            Int32 buttonEdgeLength = this.InheritedStyle.Font.Height + 5;

            // Calculate the height of the cell borders and padding.
            Rectangle borderRect = BorderWidths(
                this.DataGridView.AdjustColumnHeaderBorderStyle(
                this.DataGridView.AdvancedColumnHeadersBorderStyle,
                new DataGridViewAdvancedBorderStyle(), false, false));
            Int32 borderAndPaddingHeight = 2 +
                borderRect.Top + borderRect.Height +
                this.InheritedStyle.Padding.Vertical;
            Boolean visualStylesEnabled =
                Application.RenderWithVisualStyles &&
                this.DataGridView.EnableHeadersVisualStyles;
            if (visualStylesEnabled)
            {
                borderAndPaddingHeight += 3;
            }

            // Constrain the button edge length to the height of the
            // column headers minus the border and padding height.
            if (buttonEdgeLength >
                this.DataGridView.ColumnHeadersHeight -
                borderAndPaddingHeight)
            {
                buttonEdgeLength =
                    this.DataGridView.ColumnHeadersHeight -
                    borderAndPaddingHeight;
            }

            // Constrain the button edge length to the
            // width of the cell minus three.
            if (buttonEdgeLength > cellBounds.Width - 3)
            {
                buttonEdgeLength = cellBounds.Width - 3;
            }

            // Calculate the location of the drop-down button, with adjustments
            // based on whether visual styles are enabled.
            Int32 topOffset = visualStylesEnabled ? 4 : 1;
            Int32 top = cellBounds.Bottom - buttonEdgeLength - topOffset;
            Int32 leftOffset = visualStylesEnabled ? 3 : 1;
            Int32 left = 0;
            if (this.DataGridView.RightToLeft == RightToLeft.No)
            {
                left = cellBounds.Right - buttonEdgeLength - leftOffset;
            }
            else
            {
                left = cellBounds.Left + leftOffset;
            }

            // Set the dropDownButtonBoundsValue value using the calculated
            // values, and adjust the cell padding accordingly.
            dropDownButtonBoundsValue = new Rectangle(left, top,
                buttonEdgeLength, buttonEdgeLength);
            AdjustPadding(buttonEdgeLength + leftOffset);
        }

        /// <summary>
        /// Adjusts the cell padding to widen the header by the drop-down button width.
        /// </summary>
        /// <param name="newDropDownButtonPaddingOffset">The new drop-down button width.</param>
        private void AdjustPadding(Int32 newDropDownButtonPaddingOffset)
        {
            // Determine the difference between the new and current
            // padding adjustment.
            Int32 widthChange = newDropDownButtonPaddingOffset -
                currentDropDownButtonPaddingOffset;

            // If the padding needs to change, store the new value and
            // make the change.
            if (widthChange != 0)
            {
                // Store the offset for the drop-down button separately from
                // the padding in case the client needs additional padding.
                currentDropDownButtonPaddingOffset =
                    newDropDownButtonPaddingOffset;

                // Create a new Padding using the adjustment amount, then add it
                // to the cell's existing Style.Padding property value.
                Padding dropDownPadding = new Padding(0, 0, widthChange, 0);
                this.Style.Padding = Padding.Add(
                    this.InheritedStyle.Padding, dropDownPadding);
            }
        }

        /// <summary>
        /// The current width of the drop-down button. This field is used to adjust the cell padding.
        /// </summary>
        private Int32 currentDropDownButtonPaddingOffset;

        #endregion button bounds

        #region public properties: FilteringEnabled, AutomaticSortingEnabled, DropDownListBoxMaxLines

        /// <summary>
        /// Indicates whether filtering is enabled for the owning column.
        /// </summary>
        private Boolean filteringEnabledValue = true;

        /// <summary>
        /// Gets or sets a value indicating whether filtering is enabled.
        /// </summary>
        [DefaultValue(true)]
        public Boolean FilteringEnabled
        {
            get
            {
                // Return filteringEnabledValue if (there is no DataGridView
                // or if (its DataSource property has not been set.
                if (this.DataGridView == null ||
                    this.DataGridView.DataSource == null)
                {
                    return filteringEnabledValue;
                }

                // if (the DataSource property has been set, return a value that combines
                // the filteringEnabledValue and BindingSource.SupportsFiltering values.
                BindingSource data = this.DataGridView.DataSource as BindingSource;
                Debug.Assert(data != null);
                return filteringEnabledValue && data.SupportsFiltering;
            }
            set
            {
                // If filtering is disabled, remove the padding adjustment
                // and invalidate the button bounds.
                if (!value)
                {
                    AdjustPadding(0);
                    InvalidateDropDownButtonBounds();
                }

                filteringEnabledValue = value;
            }
        }

        /// <summary>
        /// Indicates whether automatic sorting is enabled.
        /// </summary>
        private Boolean automaticSortingEnabledValue = true;

        /// <summary>
        /// Gets or sets a value indicating whether automatic sorting is enabled for the owning column.
        /// </summary>
        [DefaultValue(true)]
        public Boolean AutomaticSortingEnabled
        {
            get
            {
                return automaticSortingEnabledValue;
            }
            set
            {
                automaticSortingEnabledValue = value;
                if (OwningColumn != null)
                {
                    if (value)
                    {
                        OwningColumn.SortMode = DataGridViewColumnSortMode.Programmatic;
                    }
                    else
                    {
                        OwningColumn.SortMode = DataGridViewColumnSortMode.NotSortable;
                    }
                }
            }
        }

        /// <summary>
        /// The maximum number of lines in the drop-down list.
        /// </summary>
        private Int32 dropDownListBoxMaxLinesValue = 30;

        /// <summary>
        /// Gets or sets the maximum number of lines to display in the drop-down filter list.
        /// The actual height of the drop-down list is constrained by the DataGridView height.
        /// </summary>
        [DefaultValue(30)]
        public Int32 DropDownListBoxMaxLines
        {
            get { return dropDownListBoxMaxLinesValue; }
            set { dropDownListBoxMaxLinesValue = value; }
        }

        #endregion public properties

        /// <summary>
        /// Represents a ListBox control used as a drop-down filter list
        /// in a DataGridView control.
        /// </summary>
        private class FilterListBox : ListBox
        {
            /// <summary>
            /// Initializes a new instance of the FilterListBox class.
            /// </summary>
            public FilterListBox()
            {
                
                Visible = false;
                IntegralHeight = true;
                BorderStyle = BorderStyle.FixedSingle;
                TabStop = false;
                Font = new Font("微软雅黑", 12.5f);
            }

            /// <summary>
            /// Indicates that the FilterListBox will handle (or ignore) all
            /// keystrokes that are not handled by the operating system.
            /// </summary>
            /// <param name="keyData">A Keys value that represents the keyboard input.</param>
            /// <returns>true in all cases.</returns>
            protected override bool IsInputKey(Keys keyData)
            {
                return true;
            }

            /// <summary>
            /// Processes a keyboard message directly, preventing it from being
            /// intercepted by the parent DataGridView control.
            /// </summary>
            /// <param name="m">A Message, passed by reference, that
            /// represents the window message to process.</param>
            /// <returns>true if the message was processed by the control;
            /// otherwise, false.</returns>
            protected override bool ProcessKeyMessage(ref Message m)
            {
                return ProcessKeyEventArgs(ref m);
            }

        }

    }

    public class DataGridViewAutoFilterTextBoxColumn : DataGridViewTextBoxColumn
    {
        /// <summary>
        /// Initializes a new instance of the DataGridViewAutoFilterTextBoxColumn class.
        /// </summary>
        public DataGridViewAutoFilterTextBoxColumn()
            : base()
        {
            base.DefaultHeaderCellType = typeof(DataGridViewAutoFilterColumnHeaderCell);
            base.SortMode = DataGridViewColumnSortMode.Programmatic;
        }

        #region public properties that hide inherited, non-virtual properties: DefaultHeaderCellType and SortMode

        /// <summary>
        /// Returns the AutoFilter header cell type. This property hides the
        /// non-virtual DefaultHeaderCellType property inherited from the
        /// DataGridViewBand class. The inherited property is set in the
        /// DataGridViewAutoFilterTextBoxColumn constructor.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never), Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public new Type DefaultHeaderCellType
        {
            get
            {
                return typeof(DataGridViewAutoFilterColumnHeaderCell);
            }
        }

        /// <summary>
        /// Gets or sets the sort mode for the column and prevents it from being
        /// set to Automatic, which would interfere with the proper functioning
        /// of the drop-down button. This property hides the non-virtual
        /// DataGridViewColumn.SortMode property from the designer. The inherited
        /// property is set in the DataGridViewAutoFilterTextBoxColumn constructor.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Advanced), Browsable(false)]
        [DefaultValue(DataGridViewColumnSortMode.Programmatic)]
        public new DataGridViewColumnSortMode SortMode
        {
            get
            {
                return base.SortMode;
            }
            set
            {
                if (value == DataGridViewColumnSortMode.Automatic)
                {
                    throw new InvalidOperationException(
                        "A SortMode value of Automatic is incompatible with " +
                        "the DataGridViewAutoFilterColumnHeaderCell type. " +
                        "Use the AutomaticSortingEnabled property instead.");
                }
                else
                {
                    base.SortMode = value;
                }
            }
        }

        #endregion

        #region public properties: FilteringEnabled, AutomaticSortingEnabled, DropDownListBoxMaxLines

        /// <summary>
        /// Gets or sets a value indicating whether filtering is enabled for this column.
        /// </summary>
        [DefaultValue(true)]
        public Boolean FilteringEnabled
        {
            get
            {
                // Return the header-cell value.
                return ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .FilteringEnabled;
            }
            set
            {
                // Set the header-cell property.
                ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .FilteringEnabled = value;
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether automatic sorting is enabled for this column.
        /// </summary>
        [DefaultValue(true)]
        public Boolean AutomaticSortingEnabled
        {
            get
            {
                // Return the header-cell value.
                return ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .AutomaticSortingEnabled;
            }
            set
            {
                // Set the header-cell property.
                ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .AutomaticSortingEnabled = value;
            }
        }

        /// <summary>
        /// Gets or sets the maximum height of the drop-down filter list for this column.
        /// </summary>
        [DefaultValue(20)]
        public Int32 DropDownListBoxMaxLines
        {
            get
            {
                // Return the header-cell value.
                return ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .DropDownListBoxMaxLines;
            }
            set
            {
                // Set the header-cell property.
                ((DataGridViewAutoFilterColumnHeaderCell)HeaderCell)
                    .DropDownListBoxMaxLines = value;
            }
        }

        #endregion public properties

        #region public, static, convenience methods: RemoveFilter and GetFilterStatus

        /// <summary>
        /// Removes the filter from the BindingSource bound to the specified DataGridView.
        /// </summary>
        /// <param name="dataGridView">The DataGridView bound to the BindingSource to unfilter.</param>
        public static void RemoveFilter(DataGridView dataGridView)
        {
            DataGridViewAutoFilterColumnHeaderCell.RemoveFilter(dataGridView);
        }

        /// <summary>
        /// Gets a status string for the specified DataGridView indicating the
        /// number of visible rows in the bound, filtered BindingSource, or
        /// String.Empty if all rows are currently visible.
        /// </summary>
        /// <param name="dataGridView">The DataGridView bound to the
        /// BindingSource to return the filter status for.</param>
        /// <returns>A string in the format "x of y records found" where x is
        /// the number of rows currently displayed and y is the number of rows
        /// available, or String.Empty if all rows are currently displayed.</returns>
        public static String GetFilterStatus(DataGridView dataGridView)
        {
            return DataGridViewAutoFilterColumnHeaderCell.GetFilterStatus(dataGridView);
        }

        #endregion
    }

    public class DataGridViewComboEditBoxColumn : DataGridViewComboBoxColumn
    {
        public DataGridViewComboEditBoxColumn()
        {
            DataGridViewComboEditBoxCell obj = new DataGridViewComboEditBoxCell();
            this.CellTemplate = obj;
        }
        public class DataGridViewComboEditBoxCell : DataGridViewComboBoxCell
        {
            public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
            {
                base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);

                ComboBox comboBox = (ComboBox)base.DataGridView.EditingControl;

                if (comboBox != null)
                {
                    comboBox.DropDownStyle = ComboBoxStyle.DropDown;
                    comboBox.AutoCompleteMode = AutoCompleteMode.Suggest;
                    comboBox.Validating += new CancelEventHandler(comboBox_Validating);
                }
            }

            //protected override object GetFormattedValue(object value, int rowIndex, ref DataGridViewCellStyle cellStyle, TypeConverter valueTypeConverter, TypeConverter formattedValueTypeConverter, DataGridViewDataErrorContexts context)
            //{
            //    if (value != null)
            //    {
            //        if (value.ToString().Trim() != string.Empty)
            //        {
            //            if (Items.IndexOf(value) == -1)
            //            {
            //                Items.Add(value);
            //                //DataGridViewComboBoxColumn col = (DataGridViewComboBoxColumn)OwningColumn;
            //                //col.Items.Add(value);
            //            }
            //        }
            //    }
            //    return base.GetFormattedValue(value, rowIndex, ref cellStyle, valueTypeConverter, formattedValueTypeConverter, context);
            //}

            void comboBox_Validating(object sender, System.ComponentModel.CancelEventArgs e)
            {
                DataGridViewComboBoxEditingControl cbo = (DataGridViewComboBoxEditingControl)sender;
                if (cbo.Text.Trim() == string.Empty) return;

                DataGridView grid = cbo.EditingControlDataGridView;
                object value = cbo.Text;

                // Add value to list if not there
                if (cbo.Items.IndexOf(value) == -1)
                {
                    DataGridViewComboBoxColumn cboCol = (DataGridViewComboBoxColumn)grid.Columns[grid.CurrentCell.ColumnIndex];
                    // Must add to both the current combobox as well as the template, to avoid duplicate entries
                    cbo.Items.Add(value);
                    //cboCol.Items.Add(value);
                    grid.CurrentCell.Value = value;
                }
            }
        }
    }
    /// <summary>
    /// 调用时所用*注意:筛选后Count数据需要用DataBindingComplete事件
    /// </summary>
    public class DataGridViewFunction
    {
        /// <summary>
        /// 筛选条件
        /// </summary>
        private static Dictionary<string, string> _listCondition = new Dictionary<string, string>();

        public static Dictionary<string, string> ListCondition
        {
            get
            {
                return _listCondition;
            }
        }

        public static void AddCondition(string key,string value)
        {
            if (_listCondition.ContainsKey(key))
            {
                _listCondition[key] = value;
            }
            else
            {
                _listCondition.Add(key, value);
            }
        }


        private static bool isFirst = true;
        /// <summary>
        /// 查询时加载时所用-生成ComBox并返回行数.每查询一次执行一次
        /// </summary>
        /// <param name="DGV"></param>
        /// <returns></returns>
        public string GridViewHeaderFilter(DataGridView DGV)
        {
            int i = 0;
            string Number = "0";//默认数量为0
            if (DGV.DataSource != null && DGV.Columns.Count > 0)
            {
                foreach (DataGridViewColumn col in DGV.Columns)
                {
                    if (i < 5)
                    {
                        col.HeaderCell = new DataGridViewAutoFilterColumnHeaderCell(col.HeaderCell);
                        i++;
                    }
                        //DataGridViewAutoFilterColumnHeaderCell.oldHeader = col.HeaderCell;
                        
                }
                //DGV.AutoResizeColumns();
                DataGridViewAutoFilterColumnHeaderCell filterCell = DGV.CurrentCell.OwningColumn.HeaderCell as DataGridViewAutoFilterColumnHeaderCell;
                if (filterCell != null && !isFirst)
                {
                    //filterCell.ShowDropDownList();
                    //e.Handled = true;
                }
                String filterStatus = DataGridViewAutoFilterColumnHeaderCell.GetFilterStatus(DGV);
                if (String.IsNullOrEmpty(filterStatus))
                {
                    Number = DGV.RowCount.ToString();
                }
                else
                {
                    Number = filterStatus;
                }
            }
            isFirst = false;
            return Number;
        }
        /// <summary>
        /// 返回GridView数据行数
        /// </summary>
        /// <param name="DGV"></param>
        /// <returns></returns>
        public string GridViewDataCount(DataGridView DGV)
        {
            return DGV.RowCount.ToString();
        }
        /// <summary>
        /// DataGridView填充数据
        /// </summary>
        /// <param name="dt"></param>
        /// <param name="DGV"></param>
        public void GridViewDataLoad(DataTable dt, DataGridView DGV)
        {
            DGV.DataSource = null;
            BindingSource dataSource = new BindingSource(dt, null);
            DGV.DataSource = dataSource;
        }

    }
}



直接新建类,将上面的代码复制进去即可使用
在填充DataGridview数据时需要如下操作

DataGridViewFunction Get = new DataGridViewFunction();
Get.GridViewDataLoad(dataTable, dataGridView); //dataTable是数据源,dataGridview是控件
Get.GridViewHeaderFilter(dataGridView);

上面的代码在网上一搜就可以搜到,我自己加了一点新功能,
这个筛选类还添加了记忆筛选条件的功能:
在筛选的过程中会保存筛选的条件,使用Dictionary<string,string>类型保存
使用时,直接用 DataGridViewFunction.ListCondition 即可,如下
Winform在DataGridView中实现下拉筛选(保存筛选条件)
筛选条件的Key就是界面上DataGridview的列名,Value就是上一次点击过的筛选条件
因为牵扯到多线程的问题,在重新给DataGridview绑定数据时会忘记筛选条件,所以添加了这个小功能,这样在下次给DataGridview绑定数据时,根据之前的筛选条件只查询筛选的值就OK

相关标签: C#.NET winform