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

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 即可,如下
在这里插入图片描述
筛选条件的Key就是界面上DataGridview的列名,Value就是上一次点击过的筛选条件
因为牵扯到多线程的问题,在重新给DataGridview绑定数据时会忘记筛选条件,所以添加了这个小功能,这样在下次给DataGridview绑定数据时,根据之前的筛选条件只查询筛选的值就OK

WinForm,如果你想在DataGridView使用下拉列表框,你可以通过监听DataGridView的CurrentCellChanged事件来实现。在事件处理程序,你可以获取当前单元格所属的列,并判断是否是需要显示下拉列表的列。如果是,你可以获取当前单元格的位置和大小,并将下拉列表框的位置和大小设置为相同。然后,你可以将单元格的内容显示为下拉列表框的当前项。最后,在下拉列表框的SelectedIndexChanged事件,你可以将选择的项更新到DataGridView相应的单元格。 以下是一个示例代码供参考: private void dataGridView1_CurrentCellChanged(object sender, EventArgs e) { DataGridViewColumn column = dataGridView1.CurrentCell.OwningColumn; if (column.Name.Equals("Column2")) // 如果是要显示下拉列表的列的话 { int columnIndex = dataGridView1.CurrentCell.ColumnIndex; int rowIndex = dataGridView1.CurrentCell.RowIndex; Rectangle rect = dataGridView1.GetCellDisplayRectangle(columnIndex, rowIndex, false); comboBox1.Left = rect.Left; comboBox1.Top = rect.Top; comboBox1.Width = rect.Width; comboBox1.Height = rect.Height; string consultingRoom = dataGridView1.Rows\[rowIndex\].Cells\[columnIndex\].Value.ToString(); int index = comboBox1.Items.IndexOf(consultingRoom); comboBox1.SelectedIndex = index; comboBox1.Visible = true; } else { comboBox1.Visible = false; } } private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) { if (dataGridView1.CurrentCell != null) { dataGridView1.CurrentCell.Value = comboBox1.Items\[comboBox1.SelectedIndex\]; } } // 将下拉列表框加入到DataGridView的控件集合内,否则下拉列表框不会显示在你点击的单元格上 dataGridView1.Controls.Add(comboBox1); 请注意,这只是一个示例代码,你需要根据你的具体需求进行适当的修改和调整。 #### 引用[.reference_title] - *1* *2* *3* [Winform datagridview显示下拉框示例](https://blog.csdn.net/Pei_hua100/article/details/124492408)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值