0

I'm currently writing a program which follows some specific design rules. For that I have created a custom ComboBox class which implements a border and a custom drop down button.

I also have a DataGridView in the main view which has one DataGridViewComboBoxColumn. Unfortunately those ComboBoxes don't follow the design rules. Is it possible to modify the DataGridViewComboBoxCell class so that it uses my custom ComboBox class? Haven't found anything online so far unfortunately.


My ComboBox class

public class FlatCombo : ComboBox {
        private const int WM_PAINT = 0xF;
        private int buttonWidth = SystemInformation.HorizontalScrollBarArrowWidth;

        public Color BorderColor { get; set; }
        public Color CircleColor { get; set; }
        public Color ButtonColor { get; set; }

        public FlatCombo() {
            BorderColor = C1;
            CircleColor = C2;
            ButtonColor = C1;
            base.BackColor = C2;
        }

        protected override void WndProc(ref Message m) {
            base.WndProc(ref m);
            if (m.Msg == WM_PAINT) {
                //Draw outlines
                using (var g = Graphics.FromHwnd(Handle)) {
                    using (var p = new Pen(BorderColor)) {
                        //Draw outlines of the dropdown menu
                        g.DrawRectangle(p, 0, 0, Width - 1, Height - 1);
                        g.DrawLine(p, Width - buttonWidth - 1, 0, Width - buttonWidth - 1, Height);
                    }
                    using (var bb = new SolidBrush(ButtonColor)) {
                        //Draw background of the dropdown button
                        g.FillRectangle(bb, Width - 17, 1, 16, Height - 2);
                    }
                    using (var cb = new SolidBrush(CircleColor)) {
                        //Draw circle of the dropdown button
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        g.FillEllipse(cb, Width - 12, (Height - 6) / 2, 5, 5);
                    }
                }
            }
        }
    }
  • Yes, I think so. But you need to follow the rules for hosting a custom cell type as outlined [here](https://learn.microsoft.com/en-us/dotnet/framework/winforms/controls/how-to-host-controls-in-windows-forms-datagridview-cells) . Quite a lot of work. – TaW Sep 04 '19 at 13:19
  • The additional work is not much of a problem. I really have no clue where to define which class is used for creating the DataGridViewCell and which classes to override etc. – Lasse Samstrøm Sep 04 '19 at 13:51
  • You'll have to study the link. – TaW Sep 04 '19 at 14:07
  • You don't add `DataGridViewComboBoxCell`, but your `DataGridViewMyComboBoxCell`. I'll try to dig for a `DataGridViewProgressCell` I have somewhere and post it bellow – Oak_3260548 Sep 05 '19 at 09:13

1 Answers1

0

Here is an example class for DataGridViewProgressColumn, which should be principally exactly what you want to achieve:

Imports System.Drawing
Imports System.ComponentModel
Public Class DataGridViewProgressColumn
    Inherits DataGridViewImageColumn
    Public Sub New()
        Me.CellTemplate = New DataGridViewProgressCell
    End Sub
End Class
Public Class DataGridViewProgressCell
    Inherits DataGridViewImageCell

    Sub New()
        ValueType = Type.GetType("Integer")
    End Sub
    ' Method required to make the Progress Cell consistent with the default Image Cell. 
    ' The default Image Cell assumes an Image as a value, although the value of the Progress Cell is an Integer.
    Protected Overrides Function GetFormattedValue( _
        ByVal value As Object, _
        ByVal rowIndex As Integer, _
        ByRef cellStyle As DataGridViewCellStyle, _
        ByVal valueTypeConverter As TypeConverter, _
        ByVal formattedValueTypeConverter As TypeConverter, _
        ByVal context As DataGridViewDataErrorContexts _
        ) As Object
        Static emptyImage As Bitmap = New Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        GetFormattedValue = emptyImage
    End Function

    Protected Overrides Sub Paint(ByVal g As System.Drawing.Graphics, ByVal clipBounds As System.Drawing.Rectangle, ByVal cellBounds As System.Drawing.Rectangle, ByVal rowIndex As Integer, ByVal cellState As System.Windows.Forms.DataGridViewElementStates, ByVal value As Object, ByVal formattedValue As Object, ByVal errorText As String, ByVal cellStyle As System.Windows.Forms.DataGridViewCellStyle, ByVal advancedBorderStyle As System.Windows.Forms.DataGridViewAdvancedBorderStyle, ByVal paintParts As System.Windows.Forms.DataGridViewPaintParts)
        Try

            Dim progressVal As Integer = CType(IIf(IsDBNull(value), 0, value), Integer)
            Dim percentage As Single = CType((progressVal / 100), Single)
            Dim backBrush As Brush = New SolidBrush(cellStyle.BackColor)
            Dim foreBrush As Brush = New SolidBrush(cellStyle.ForeColor)
            ' Call the base class method to paint the default cell appearance.
            MyBase.Paint(g, clipBounds, cellBounds, rowIndex, cellState, _
                value, formattedValue, errorText, cellStyle, _
                advancedBorderStyle, paintParts)
            If percentage > 0.0 Then
                ' Draw the progress bar and the text
                g.FillRectangle(New SolidBrush(Color.FromArgb(163, 189, 242)), cellBounds.X + 2, cellBounds.Y + 2, Convert.ToInt32((percentage * cellBounds.Width - 4)), cellBounds.Height - 4)
                g.DrawString(progressVal.ToString() & "%", cellStyle.Font, foreBrush, cellBounds.X + 6, cellBounds.Y + 2)
            Else
                'draw the text
                If Not Me.DataGridView.CurrentCell Is Nothing AndAlso Me.DataGridView.CurrentCell.RowIndex = rowIndex Then
                    g.DrawString(progressVal.ToString() & "%", cellStyle.Font, New SolidBrush(cellStyle.SelectionForeColor), cellBounds.X + 6, cellBounds.Y + 2)
                Else
                    g.DrawString(progressVal.ToString() & "%", cellStyle.Font, foreBrush, cellBounds.X + 6, cellBounds.Y + 2)
                End If
            End If
        Catch ex As Exception
            MsgBox("Graf nemohl být načten.")
        End Try
    End Sub
End Class

Define your Columns and add them to the DataGridView (you can do it in the GUI as well!):

Dim WIcol0 As New DataGridViewTextBoxColumn
Dim WIcol1 As New DataGridViewTextBoxColumn
Dim WIcol2 As New DataGridViewTextBoxColumn
Dim WIcol3 As New DataGridViewTextBoxColumn
Dim WIcol4 As New DataGridViewTextBoxColumn
Dim WIcol4a As New DataGridViewProgressColumn   ' <<< Define a column of your type

            With WIcol0
                .Width = "45"
                .HeaderText = "#"
                .DataPropertyName = "RowNo"
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
                .DefaultCellStyle.Padding = New Padding(6, 0, 0, 0)
                .Name = .DataPropertyName
                .Visible = True
                .ReadOnly = True
            End With
            With WIcol1
                .Width = "250"
                .HeaderText = "Company Name"
                .DataPropertyName = "CompanyName"
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleLeft
                .DefaultCellStyle.Padding = New Padding(6, 0, 0, 0)
                .Name = .DataPropertyName
                .Visible = True
                .ReadOnly = True
            End With
            With WIcol2
                .Width = "100"
                .HeaderText = "Turnover"
                .DataPropertyName = "Turnover"
                .DefaultCellStyle.Format = "# ### ##0.00 Kč"
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                .Name = .DataPropertyName
                .ReadOnly = True
            End With
            With WIcol3
                .Width = "80"
                .HeaderText = "Running Time"
                .DataPropertyName = "RunningTime"
                .DefaultCellStyle.Format = "0 dní"
                '.DefaultCellStyle.Padding = New Padding(6, 0, 0, 0)
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                .Name = .DataPropertyName
                .ReadOnly = True
            End With
            With WIcol4
                .Width = "80"
                .HeaderText = "After"
                .DataPropertyName = "After"
                .DefaultCellStyle.Format = "0.0 dní"
                .DefaultCellStyle.Font = New Font(Me.dgwOTD.Font, FontStyle.Bold)
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                .Name = .DataPropertyName
                .ReadOnly = True
            End With
            With WIcol4a
                .Width = "80"
                .HeaderText = "Performance"
                .DataPropertyName = "Performance"
                '.DefaultCellStyle.Format = "0 dní"
                .DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight
                .DefaultCellStyle.ForeColor = Color.LightGray
                .Name = .DataPropertyName
                .DefaultCellStyle.Format = ""
                .ReadOnly = True
            End With    

EDIT: I Just realized that it's VB.NET and you have C#, but the logic is identical of course. If you have difficulty to read it, just post a comment, I'd include translated C# code.

Oak_3260548
  • 1,882
  • 3
  • 23
  • 41