How to have Checkbox as ColumnHeader of dataGridView

前端 未结 6 1977
鱼传尺愫
鱼传尺愫 2020-12-24 04:00

I\'m developing a window application in C# VS2005. I have a dataGridView in which the first column has Checkboxes. Now i want the Column header also to be a CheckBox which i

相关标签:
6条回答
  • 2020-12-24 04:09

    If you choose @Ehsan solution you must know that if the checkbox cell is selected it won't update the checkbox UI. You need to call RefreshEdit() afterwards.

    It only happens when the control is child-control of the datagridview. For some reason when it is a child-control the selected cell checkboxes are not able to refresh the UI by their own.

    0 讨论(0)
  • 2020-12-24 04:20

    I also needed to have a CheckBox in the column header of a DataGridView column. Here's how I did it:

    • Create a class which inherits from DataGridViewColumnHeaderCell
    • Internally use a System.Windows.Forms.CheckBox to store the checked state and provide the OS styled visual CheckBox representation.
    • Use a Bitmap as a buffer and draw the regular CheckBox on it (using CheckBox.DrawToBitmap)
    • Override DataGridViewColumnHeaderCell.Paint and, if necessary, update the buffer before drawing the buffer to the Graphics supplied by Paint
    • Provide a Checked property on the derived DataGridViewColumnHeaderCell, and also a CheckedChanged event
    • Substitute the derived DataGridViewColumnHeaderCell in the column's HeaderCell when the DataGridView is being populated.
    • Check and uncheck the CheckBox when it is the column header is clicked, only if the mouse click is within the bounds of the CheckBox
    • Implement the check-all/uncheck-all outside of the derived class by listening to the CheckedChanged event, updating the underlying data object and then calling ResetBindings to update the DataGridView

    Here's the class I wrote which is derived from DataGridViewColumnHeaderCell:

    class DataGridViewCheckBoxColumnHeaderCell : DataGridViewColumnHeaderCell
    {
        private Bitmap buffer;
        private CheckBox checkBox;
        private Rectangle checkBoxBounds;
    
        public DataGridViewCheckBoxColumnHeaderCell()
        {
            this.checkBox = new CheckBox();
        }
    
        public event EventHandler CheckedChanged;
    
        public bool Checked
        {
            get 
            { 
                return this.checkBox.Checked; 
            }
    
            set
            {
                if (!this.Checked == value)
                {
                    this.checkBox.Checked = value;
                    if (this.buffer != null)
                    {
                        this.buffer.Dispose();
                        this.buffer = null;
                    }
    
                    this.OnCheckedChanged(EventArgs.Empty);
    
                    if (this.DataGridView != null)
                    {
                        this.DataGridView.Refresh();
                    }
                }
            }
        }
    
        protected override void Paint(
            Graphics graphics, 
            Rectangle clipBounds, 
            Rectangle cellBounds, 
            int rowIndex, 
            DataGridViewElementStates dataGridViewElementState, 
            object value, 
            object formattedValue, 
            string errorText, 
            DataGridViewCellStyle cellStyle, 
            DataGridViewAdvancedBorderStyle advancedBorderStyle, 
            DataGridViewPaintParts paintParts)
        {
            // Passing String.Empty in place of 
            // value and formattedValue prevents 
            // this header cell from having text.
    
            base.Paint(
                graphics, 
                clipBounds, 
                cellBounds, 
                rowIndex, 
                dataGridViewElementState, 
                String.Empty, 
                String.Empty, 
                errorText, 
                cellStyle, 
                advancedBorderStyle, 
                paintParts);
    
            if (this.buffer == null 
                || cellBounds.Width != this.buffer.Width
                || cellBounds.Height != this.buffer.Height)
            {
                this.UpdateBuffer(cellBounds.Size);
            }
    
            graphics.DrawImage(this.buffer, cellBounds.Location);
        }
    
        protected override Size GetPreferredSize(
            Graphics graphics, 
            DataGridViewCellStyle cellStyle, 
            int rowIndex, 
            Size constraintSize)
        {
            return this.checkBox.GetPreferredSize(constraintSize);
        }
    
        protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left 
                && this.checkBoxBounds.Contains(e.Location))
            {
                this.Checked = !this.Checked;
            }
    
            base.OnMouseClick(e);
        }
    
        private void UpdateBuffer(Size size)
        {
            Bitmap updatedBuffer = new Bitmap(size.Width, size.Height);
    
            this.checkBox.Size = size;
    
            if (this.checkBox.Size.Width > 0 && this.checkBox.Size.Height > 0)
            {
                Bitmap renderedCheckbox = new Bitmap(
                    this.checkBox.Width, 
                    this.checkBox.Height);
    
                this.checkBox.DrawToBitmap(
                    renderedCheckbox, 
                    new Rectangle(new Point(), this.checkBox.Size));
    
                MakeTransparent(renderedCheckbox, this.checkBox.BackColor);
                Bitmap croppedRenderedCheckbox = AutoCrop(
                    renderedCheckbox, 
                    Color.Transparent);
    
                // TODO implement alignment, right now it is always
                // MiddleCenter regardless of this.Style.Alignment
    
                this.checkBox.Location = new Point(
                    (updatedBuffer.Width - croppedRenderedCheckbox.Width) / 2, 
                    (updatedBuffer.Height - croppedRenderedCheckbox.Height) / 2);
    
                Graphics updatedBufferGraphics = Graphics.FromImage(updatedBuffer);
                updatedBufferGraphics.DrawImage(
                    croppedRenderedCheckbox, 
                    this.checkBox.Location);
    
                this.checkBoxBounds = new Rectangle(
                    this.checkBox.Location, 
                    croppedRenderedCheckbox.Size);
    
                renderedCheckbox.Dispose();
                croppedRenderedCheckbox.Dispose();
            }
    
            if (this.buffer != null)
            {
                this.buffer.Dispose();
            }
    
            this.buffer = updatedBuffer;
        }
    
        protected virtual void OnCheckedChanged(EventArgs e)
        {
            EventHandler handler = this.CheckedChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }
    
        // The methods below are helper methods for manipulating Bitmaps
    
        private static void MakeTransparent(Bitmap bitmap, Color transparencyMask)
        {
            int transparencyMaskArgb = transparencyMask.ToArgb();
            int transparentArgb = Color.Transparent.ToArgb();
    
            List deadColumns = new List();
    
            for (int x = 0; x = 0; x--)
            {
                if (deadColumns.Count == bitmap.Height)
                {
                    break;
                }
    
                for (int y = bitmap.Height - 1; y >= 0; y--)
                {
                    if (deadColumns.Contains(y))
                    {
                        continue;
                    }
    
                    int pixel = bitmap.GetPixel(x, y).ToArgb();
    
                    if (pixel == transparencyMaskArgb)
                    {
                        bitmap.SetPixel(x, y, Color.Transparent);
                    }
                    else if (pixel != transparentArgb)
                    {
                        deadColumns.Add(y);
                        break;
                    }
                }
            }
        }
    
        public static Bitmap AutoCrop(Bitmap bitmap, Color backgroundColor)
        {
            Size croppedSize = bitmap.Size;
            Point cropOrigin = new Point();
            int backgroundColorToArgb = backgroundColor.ToArgb();
    
            for (int x = bitmap.Width - 1; x >= 0; x--)
            {
                bool allPixelsAreBackgroundColor = true;
                for (int y = bitmap.Height - 1; y >= 0; y--)
                {
                    if (bitmap.GetPixel(x, y).ToArgb() != backgroundColorToArgb)
                    {
                        allPixelsAreBackgroundColor = false;
                        break;
                    }
                }
    
                if (allPixelsAreBackgroundColor)
                {
                    croppedSize.Width--;
                }
                else
                {
                    break;
                }
            }
    
            for (int y = bitmap.Height - 1; y >= 0; y--)
            {
                bool allPixelsAreBackgroundColor = true;
                for (int x = bitmap.Width - 1; x >= 0; x--)
                {
                    if (bitmap.GetPixel(x, y).ToArgb() != backgroundColorToArgb)
                    {
                        allPixelsAreBackgroundColor = false;
                        break;
                    }
                }
    
                if (allPixelsAreBackgroundColor)
                {
                    croppedSize.Height--;
                }
                else
                {
                    break;
                }
            }
    
            for (int x = 0; x = 0 && xWhole = 0)
                    {
                        bitmapSection.SetPixel(x, y, bitmap.GetPixel(xWhole, yWhole));
                    }
                    else
                    {
                        bitmapSection.SetPixel(x, y, Color.Transparent);
                    }
                }
            }
    
            return bitmapSection;
        }
    }
    
    0 讨论(0)
  • 2020-12-24 04:24

    So - to address the top-most checkbox not showing as checked (even though it is), I ended up editing your event as follows:

    private void checkboxHeader_CheckedChanged(object sender, EventArgs e) { //CheckBox headerBox = ((CheckBox)dtgv1.Controls.Find("checkboxHeader", true)[0]);

            var headerBox = (CheckBox)sender;
            var b = headerBox.Checked;
            var c = int.Parse(headerBox.Name.Replace("checkboxHeader", ""));
            for (int i = 0; i < dgvSources_fuzzyID.RowCount; i++)
            {
                dgvSources_fuzzyID.Rows[i].Cells[0].Value = headerBox.Checked;
                ***dgvSources_fuzzyID.RefreshEdit();***
            }
        }
    

    That is what finally fixed it for me...

    0 讨论(0)
  • 2020-12-24 04:29

    Thanks Ehsan for the easy way, works fine for me. For the problem only one CheckBox is always at the upper left corner, I add a name suffix to each one and solved. In addition, I would like to slightly modify the event handling method by directly cast the object sender since it was very specific up on the creation (unless this method is used to handling other kind of controls' event either).

    private string[] _colLst = columNameArray;
    private void AddCheckBoxGridViewHeader()
    {
        for (int ndx = 0; ndx < _colLst.Length; ndx++)
        {
            var rect = dtgv1.GetCellDisplayRectangle(ndx, -1, true);
            var x = rect.X + (rect.Width * 4 / 5);
            var y = 3;
            Rectangle nrect = new Rectangle(x, y, rect.Width, rect.Height);
            CheckBox checkboxHeader = new CheckBox();
            checkboxHeader.BackColor = Color.Transparent;
            checkboxHeader.Name = "checkboxHeader" + ndx;
            checkboxHeader.Size = new Size(18, 18);
            checkboxHeader.Location = nrect.Location;
            checkboxHeader.CheckedChanged += new EventHandler(checkboxHeader_CheckedChanged);
            dtgv1.Controls.Add(checkboxHeader);
        }
    }
    
    private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
    {
        //CheckBox headerBox = ((CheckBox)dtgv1.Controls.Find("checkboxHeader", true)[0]);
    
        var headerBox = (CheckBox)sender;
        var b = headerBox.Checked;
        var c = int.Parse(headerBox.Name.Replace("checkboxHeader", ""));
        for (int i = 0; i < dtgv1.RowCount; i++)
        {
            dtgv1.Rows[i].Cells[c].Style = new DataGridViewCellStyle(); 
            dtgv1.Rows[i].Cells[c].Style.BackColor = (b)? Color.Salmon : Color.White;
        }
    }
    
    0 讨论(0)
  • 2020-12-24 04:32

    The above solution is kind of good, but there is also an easier way! Simply add these two methods and then you would have what you want!

    First add a show_chkBox method to your code and call it in the onload function of your form or after creating your DataGridView:

     private void show_chkBox()
     {
         Rectangle rect = dataGridView1.GetCellDisplayRectangle(0, -1, true);
         // set checkbox header to center of header cell. +1 pixel to position 
         rect.Y = 3;
         rect.X = rect.Location.X + (rect.Width/4);
         CheckBox checkboxHeader = new CheckBox();
         checkboxHeader.Name = "checkboxHeader";
         //datagridview[0, 0].ToolTipText = "sdfsdf";
         checkboxHeader.Size = new Size(18, 18);
         checkboxHeader.Location = rect.Location;
         checkboxHeader.CheckedChanged += new EventHandler(checkboxHeader_CheckedChanged);
         dataGridView1.Controls.Add(checkboxHeader);
     }
    

    and then you would have the checkbox in the header.

    For the selecting problem, just add this code:

    private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
    {             
        CheckBox headerBox = ((CheckBox)dataGridView1.Controls.Find("checkboxHeader", true)[0]);
        int index = 0;
        for (int i = 0; i < dataGridView1.RowCount; i++)
        {
            dataGridView1.Rows[i].Cells[0].Value = headerBox.Checked;
        }
    }
    
    0 讨论(0)
  • 2020-12-24 04:33

    Following is the solution that worked for me as Ehsaan's solution was not checking the first row in the DataGridView.

    Call the AddChkBoxHeader_DataGridView() method in OnLoad function of your form.

    CheckBox checkboxHeader = null;
    bool isHeaderCheckBoxClicked = false;
    private void AddChkBoxHeader_DataGridView()
    {
        Rectangle rect = dgvRecipeSelector.GetCellDisplayRectangle(0, -1, true);
        rect.Y = 10;
        rect.X = rect.Location.X + (rect.Width / 4);            
        checkboxHeader = new CheckBox();
        checkboxHeader.Size = new Size(15,15);
        checkboxHeader.Location = rect.Location;
        dgvRecipeSelector.Controls.Add(checkboxHeader);
        checkboxHeader.MouseClick += new MouseEventHandler(checkboxHeader_CheckedChanged);
    }
    
    private void checkboxHeader_CheckedChanged(object sender, EventArgs e)
    {            
        HeaderCheckBoxClick((CheckBox)sender);
    }
    
    private void HeaderCheckBoxClick(CheckBox headerCheckbox)
    {
        isHeaderCheckBoxClicked = true;
        foreach (DataGridViewRow r in dgvRecipeSelector.Rows)
        {
            ((DataGridViewCheckBoxCell)r.Cells[0]).Value = headerCheckbox.Checked;
        }
        dgvRecipeSelector.RefreshEdit();
        isHeaderCheckBoxClicked = false;
    }
    
    0 讨论(0)
提交回复
热议问题