Custom combo box is updated on getting focus but cleared on loosing focus

Viewed 49

I have built a custom combo box control (dgvcomboCustom) that adjusts its height to the height of the datagrid row -> that work fine.

Unfortunately, this custom combo box is properly updated only on getting focus but its content is cleared [value & style:ComboBoxStyle.DropDownList] on loosing focus.

Note This custom combo box is the c# porting of this VB example: DataGridViewComboBoxColumn odd behaviour, temporarily resizing on CellEnter

How I can have this user combo box control (dgvcomboCustom) retains its last value and style (ComboBoxStyle.DropDownList) that it got before loosing the focus?

Here is the sample code with three controls in order to better understand the issue I met with dgvcomboCustom:

dgvcomboCustom -> user combo box control that is updated properly on getting focus but its content is cleared on loosing focus - Having focus -> Key Tab / Loosing focus -> Key [SHIFT + Tab]

calendar -> user calendar control that is updated properly on getting focus and loosing focus

dgvcomboRegular -> regular combo box that is updated properly on getting focus and loosing focus

using System;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsApp2
{
    public partial class Form1_Import : Form
    {
        string[] strArray = new string[] { "Mahesh Chand", "Mike Gold", "Raj Beniwal", "Praveen Kumar", "Dinesh Beniwal" };
        public Form1_Import()
        {
            InitializeComponent();

            dataGridView2.AutoGenerateColumns = true;
            dataGridView2.RowTemplate.Height = 45;

            CalendarColumn calendar = new CalendarColumn();
            calendar.HeaderText = "Calendar";
            calendar.Name = "Calendar";
            calendar.Width = 100;
            this.dataGridView2.Columns.Add(calendar);

            CustomComboBoxColumn dgvcomboCustom = new CustomComboBoxColumn();
            dgvcomboCustom.HeaderText = "Custom";
            dgvcomboCustom.Name = "Custom";
            dgvcomboCustom.Width = 150;
            dgvcomboCustom.DataSource = strArray;
            dataGridView2.Columns.Add(dgvcomboCustom);

            DataGridViewComboBoxColumn dgvcomboRegular = new DataGridViewComboBoxColumn();
            dgvcomboRegular.Items.Add("Item1 regular");
            dgvcomboRegular.Items.Add("Item2 regular");
            dgvcomboRegular.HeaderText = "Regular";
            dgvcomboRegular.Name = "Regular";
            dgvcomboRegular.Width = 100;
            dataGridView2.Columns.Add(dgvcomboRegular);
        }
    }

    #region Custom Combobox which content is cleared on loosin focus
    public partial class CustomComboBox : ComboBox
    {
        public CustomComboBox()
        {
            ItemHeight = 26;
            DropDownHeight = ItemHeight * 6;
        }

        protected override void OnDrawItem(DrawItemEventArgs e)
        {
            if ((e.Index >= 0))
            {
                e.DrawBackground();
                e.DrawFocusRectangle();

                using (SolidBrush b = new SolidBrush(ForeColor))
                {
                    //ComboBox combo = sender as ComboBox;
                    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                        e.Graphics.FillRectangle(new SolidBrush(Color.Beige), e.Bounds);
                    else
                        e.Graphics.FillRectangle(new SolidBrush(BackColor), e.Bounds);

                    string name = Items[e.Index].ToString();

                    Int32 textLeft = e.Bounds.Left + 3;
                    Int32 textTop = e.Bounds.Top + (e.Bounds.Height / 2) - (Font.Height / 2);
                    Int32 textWidth = e.Bounds.Width - e.Bounds.Width;
                    Int32 textHeight = e.Bounds.Height;
                    Rectangle textTarget = new Rectangle(textLeft, textTop, textWidth, textHeight);

                    e.Graphics.DrawString(name, Font, b, textTarget);
                }
            }
        }
    }

    class CustomComboBoxEditControl : CustomComboBox, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;
        public CustomComboBoxEditControl() : base()
        {           
            Console.WriteLine("CustomComboBoxEditControl");
            EditingControlFormattedValue = false;

            DropDownStyle = ComboBoxStyle.DropDownList;
            DrawMode = DrawMode.OwnerDrawFixed;
        }

        protected override void OnLostFocus(EventArgs e)
        {
            Console.WriteLine("OnLostFocus");
        }

        // Implements the IDataGridViewEditingControl.EditingControlFormattedValue    
        // property.   
        public object EditingControlFormattedValue
        {
            get { return SelectedItem; }
            set { SelectedItem = value; }
        }

        // Implements the    
        // IDataGridViewEditingControl.GetEditingControlFormattedValue method.   
        public object GetEditingControlFormattedValue( DataGridViewDataErrorContexts context )
        {
            return EditingControlFormattedValue;
        }

        // Implements the    
        // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.   
        public void ApplyCellStyleToEditingControl(DataGridViewCellStyle dataGridViewCellStyle)
        {
            Font = dataGridViewCellStyle.Font;
            ForeColor = dataGridViewCellStyle.ForeColor;
            BackColor = dataGridViewCellStyle.BackColor;
        }

        // Implements the IDataGridViewEditingControl.EditingControlRowIndex    
        // property.   
        public int EditingControlRowIndex
        {
            get
            {
                return rowIndex;
            }
            set
            {
                rowIndex = value;
            }
        }

        // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey    
        // method.   
        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            // Let the DateTimePicker handle the keys listed.   
            switch (key & Keys.KeyCode)
            {
                case Keys.Left:
                case Keys.Up:
                case Keys.Down:
                case Keys.Right:
                case Keys.Home:
                case Keys.End:
                case Keys.PageDown:
                case Keys.PageUp:
                case Keys.Tab:
                    return true;
                default:
                    return !dataGridViewWantsInputKey;
            }
        }

        // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
        // method.
        public void PrepareEditingControlForEdit(bool selectAll)
        {
            // No preparation needs to be done.
        }

        // Implements the IDataGridViewEditingControl   
        // .RepositionEditingControlOnValueChange property.   
        public bool RepositionEditingControlOnValueChange
        {
            get { return false; }
        }

        public DataGridView EditingControlDataGridView
        {
            get
            {
                return dataGridView;
            }
            set
            {
                dataGridView = value;
            }
        }

        // Implements the IDataGridViewEditingControl   
        // .EditingControlValueChanged property.   
        public bool EditingControlValueChanged
        {
            get
            {
                return valueChanged;
            }
            set
            {
                valueChanged = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingPanelCursor property.
        public Cursor EditingPanelCursor
        {
            get { return base.Cursor; }
        }
    }

    public class CustomComboBoxColumn : DataGridViewColumn
    {
        public CustomComboBoxColumn() : base (new CustomComboBoxCell()) {}

        public object DataSource { get; set; }

        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }

            set
            {
                Type targetType = typeof(CustomComboBoxCell);
                // Ensure that the cell used for the template is a CustomComboBoxCell. 
                if ((!(value == null) && !value.GetType().IsAssignableFrom(targetType)))
                {
                    string errorMessage = $"CellTemplate must be of the type {targetType}.";
                    throw new InvalidCastException(errorMessage);
                }

                base.CellTemplate = value;
            }
        }

        public override object Clone()
        {
            CustomComboBoxColumn retVal = (CustomComboBoxColumn)base.Clone();
            retVal.DataSource = this.DataSource;

            return retVal;
        }
    }

    public class CustomComboBoxCell : DataGridViewTextBoxCell
    {
        string strElement;
        string strDefault;

        public CustomComboBoxCell() : base()
        {
        }

        public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            // Set the value of the editing control to the current cell value.   
            base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);

            CustomComboBoxEditControl ctl = (CustomComboBoxEditControl)DataGridView.EditingControl;
            CustomComboBoxColumn col = (CustomComboBoxColumn)DataGridView.Columns[DataGridView.CurrentCell.ColumnIndex];
            DataGridViewRow row = (DataGridViewRow)DataGridView.Rows[DataGridView.CurrentCell.RowIndex];

            string[] strArray = (string[])col.DataSource;
            strElement = strArray[0];
            ctl.DataSource = col.DataSource;
            ctl.Height = DataGridView.RowTemplate.Height;
            ctl.ItemHeight = row.Height - 6;

            // Use the default row value when Value property is null. 
            if ((this.Value == null || this.Value == DBNull.Value))
                ctl.Text = (string)this.DefaultNewRowValue;
            else
                ctl.Text = (string)this.Value;
        }

        public override Type EditType
        {
            get { return typeof(CustomComboBoxEditControl); }
        }

        public override Type FormattedValueType
        {
            get
            {
                return typeof(string);
            }
        }

        public override Type ValueType
        {
            get
            {
                return typeof(string);
            }
        }

        public override object DefaultNewRowValue
        {
            get
            {
                return string.Empty;
            }
        }

        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)
        {
            base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
            graphics.FillRectangle(new SolidBrush(cellStyle.BackColor), cellBounds);

            string valueString = Convert.ToString(value);

            Brush b = new SolidBrush(cellStyle.ForeColor);

            Int32 textLeft = cellBounds.Left + 6;
            Int32 textTop = cellBounds.Top + (cellBounds.Height / 2) - (cellStyle.Font.Height / 2);
            Int32 textWidth = cellBounds.Width - cellBounds.Width;
            Int32 textHeight = cellBounds.Height;
            Rectangle textTarget = new Rectangle(textLeft, textTop, textWidth, textHeight);

            graphics.DrawString(valueString, cellStyle.Font, b, textTarget);
        }
    }
    #endregion

    #region Calendar

    public class CalendarColumn : DataGridViewColumn
    {
        public CalendarColumn() : base(new CalendarCell())
        {
        }

        public override DataGridViewCell CellTemplate
        {
            get
            {
                return base.CellTemplate;
            }
            set
            {
                // Ensure that the cell used for the template is a CalendarCell.
                if (value != null &&
                    !value.GetType().IsAssignableFrom(typeof(CalendarCell)))
                {
                    throw new InvalidCastException("Must be a CalendarCell");
                }
                base.CellTemplate = value;
            }
        }
    }

    public class CalendarCell : DataGridViewTextBoxCell
    {

        public CalendarCell()
            : base()
        {
            // Use the short date format.
            this.Style.Format = "d";
        }

        public override void InitializeEditingControl(int rowIndex, object
            initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            // Set the value of the editing control to the current cell value.
            base.InitializeEditingControl(rowIndex, initialFormattedValue,
                dataGridViewCellStyle);
            CalendarEditingControl ctl =
                DataGridView.EditingControl as CalendarEditingControl;
            // Use the default row value when Value property is null.
            if (this.Value == null)
            {
                ctl.Value = (DateTime)this.DefaultNewRowValue;
            }
            else
            {
                ctl.Value = (DateTime)this.Value;
            }
        }

        public override Type EditType
        {
            get
            {
                // Return the type of the editing control that CalendarCell uses.
                return typeof(CalendarEditingControl);
            }
        }

        public override Type ValueType
        {
            get
            {
                // Return the type of the value that CalendarCell contains.

                return typeof(DateTime);
            }
        }

        public override object DefaultNewRowValue
        {
            get
            {
                // Use the current date and time as the default value.
                return DateTime.Now;
            }
        }
    }

    class CalendarEditingControl : DateTimePicker, IDataGridViewEditingControl
    {
        DataGridView dataGridView;
        private bool valueChanged = false;
        int rowIndex;

        public CalendarEditingControl()
        {
            this.Format = DateTimePickerFormat.Short;
        }

        // Implements the IDataGridViewEditingControl.EditingControlFormattedValue 
        // property.
        public object EditingControlFormattedValue
        {
            get
            {
                return this.Value.ToShortDateString();
            }
            set
            {
                if (value is String)
                {
                    try
                    {
                        // This will throw an exception of the string is 
                        // null, empty, or not in the format of a date.
                        this.Value = DateTime.Parse((String)value);
                    }
                    catch
                    {
                        // In the case of an exception, just use the 
                        // default value so we're not left with a null
                        // value.
                        this.Value = DateTime.Now;
                    }
                }
            }
        }

        // Implements the 
        // IDataGridViewEditingControl.GetEditingControlFormattedValue method.
        public object GetEditingControlFormattedValue(
            DataGridViewDataErrorContexts context)
        {
            return EditingControlFormattedValue;
        }

        // Implements the 
        // IDataGridViewEditingControl.ApplyCellStyleToEditingControl method.
        public void ApplyCellStyleToEditingControl(
            DataGridViewCellStyle dataGridViewCellStyle)
        {
            this.Font = dataGridViewCellStyle.Font;
            this.CalendarForeColor = dataGridViewCellStyle.ForeColor;
            this.CalendarMonthBackground = dataGridViewCellStyle.BackColor;
        }

        // Implements the IDataGridViewEditingControl.EditingControlRowIndex 
        // property.
        public int EditingControlRowIndex
        {
            get
            {
                return rowIndex;
            }
            set
            {
                rowIndex = value;
            }
        }

        // Implements the IDataGridViewEditingControl.EditingControlWantsInputKey 
        // method.
        public bool EditingControlWantsInputKey(
            Keys key, bool dataGridViewWantsInputKey)
        {
            // Let the DateTimePicker handle the keys listed.
        switch (key & Keys.KeyCode)
            {
                case Keys.Left:
                case Keys.Up:
                case Keys.Down:
                case Keys.Right:
                case Keys.Home:
                case Keys.End:
                case Keys.PageDown:
                case Keys.PageUp:
                    return true;
                default:
                return !dataGridViewWantsInputKey;
            }
        }

        // Implements the IDataGridViewEditingControl.PrepareEditingControlForEdit 
        // method.
        public void PrepareEditingControlForEdit(bool selectAll)
        {
            // No preparation needs to be done.
        }

        // Implements the IDataGridViewEditingControl
        // .RepositionEditingControlOnValueChange property.
        public bool RepositionEditingControlOnValueChange
        {
            get
            {
                return false;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlDataGridView property.
        public DataGridView EditingControlDataGridView
        {
            get
            {
                return dataGridView;
            }
            set
            {
                dataGridView = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingControlValueChanged property.
        public bool EditingControlValueChanged
        {
            get
            {
                return valueChanged;
            }
            set
            {
                valueChanged = value;
            }
        }

        // Implements the IDataGridViewEditingControl
        // .EditingPanelCursor property.
        public Cursor EditingPanelCursor
        {
            get
            {
                return base.Cursor;
            }
        }

        protected override void OnValueChanged(EventArgs eventargs)
        {
            // Notify the DataGridView that the contents of the cell
            // have changed.
            valueChanged = true;
            this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
            base.OnValueChanged(eventargs);
        }
    }
    #endregion
}
0 Answers
Related