// Fixed AdvancedRadMultiColumnComboPlus - Designer Compatible Version using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; using Telerik.WinControls; using Telerik.WinControls.Data; using Telerik.WinControls.UI; namespace CustomComponents { [ToolboxItem(true)] [DesignerCategory("Component")] [Designer("System.Windows.Forms.Design.ParentControlDesigner, System.Design")] [Description("Advanced RadMultiColumnComboBox with enhanced selection handling")] public partial class AdvancedRadMultiColumnComboPlus : UserControl { #region Nested Classes [Serializable] public class ColumnConfig { public string Name { get; set; } = string.Empty; public string HeaderText { get; set; } = string.Empty; public int Width { get; set; } = -1; // -1 means auto-size public ContentAlignment HeaderAlignment { get; set; } = ContentAlignment.MiddleLeft; public ContentAlignment TextAlignment { get; set; } = ContentAlignment.MiddleLeft; } public class ComponentException : Exception { public ComponentException(string message) : base(message) { } public ComponentException(string message, Exception inner) : base(message, inner) { } } #endregion #region Fields - Design Time Safe private RadMultiColumnComboBox _comboBox; private Label _headerLabel; // State management private bool _isDropDownOpen; private int _currentIndex = -1; private bool _isManualNavigation; private bool _isInitializing; // Configuration private string _headerText = string.Empty; private List _columnsConfig; // Styling properties with backing fields private Font _itemFont; private Color _itemForeColor = SystemColors.ControlText; private Color _itemBackColor = SystemColors.Window; private Color _highlightColor = Color.FromArgb(201, 252, 254); private int _itemRowHeight = 32; private ContentAlignment _itemTextAlign = ContentAlignment.MiddleRight; // Cached values for performance private string _cachedDisplayMember; private string _cachedValueMember; #endregion #region Events [Category("Action")] [Description("Occurs when the selected value changes")] public event EventHandler SelectedValueChanged; [Category("Action")] [Description("Occurs during navigation before final selection")] public event EventHandler ProvisionalSelectionChanged; [Category("Action")] [Description("Occurs when dropdown opens")] public event EventHandler DropDownOpened; [Category("Action")] [Description("Occurs when dropdown closes")] public event EventHandler DropDownClosed; #endregion #region Constructor public AdvancedRadMultiColumnComboPlus() { InitializeControlDefaults(); //InitializeComponent(); InitializeCustomComponent(); } // Initialize default values before InitializeComponent private void InitializeControlDefaults() { _isInitializing = true; _itemFont = new Font("Tahoma", 9); _columnsConfig = new List(); } // Initialize after Designer-generated InitializeComponent private void InitializeCustomComponent() { try { if (DesignMode) { _isInitializing = false; return; } CreateCustomControls(); SetupControlProperties(); WireEvents(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"InitializeCustomComponent error: {ex.Message}"); } finally { _isInitializing = false; } } #endregion #region Public Properties - Data [Category("Data")] [Description("Column configuration settings")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] public List ColumnsConfig { get => _columnsConfig ?? (_columnsConfig = new List()); set { _columnsConfig = value ?? new List(); if (!_isInitializing && !DesignMode && _comboBox?.EditorControl?.ColumnCount > 0) { ApplyColumnConfigurations(); } } } [Category("Data")] [AttributeProvider(typeof(IListSource))] [Description("Data source for the combo box")] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Browsable(false)] public object DataSource { get => _comboBox?.DataSource; set => SetDataSourceSafe(value); } [Category("Data")] [Description("Property to display in the combo box")] [DefaultValue("")] public string DisplayMember { get => _cachedDisplayMember ?? (_cachedDisplayMember = _comboBox?.DisplayMember ?? string.Empty); set { if (_cachedDisplayMember != value) { _cachedDisplayMember = value; if (_comboBox != null && !DesignMode) { _comboBox.DisplayMember = value; ConfigureFiltering(); } } } } [Category("Data")] [Description("Property to use as value")] [DefaultValue("")] public string ValueMember { get => _cachedValueMember ?? (_cachedValueMember = _comboBox?.ValueMember ?? string.Empty); set { if (_cachedValueMember != value) { _cachedValueMember = value; if (_comboBox != null && !DesignMode) { _comboBox.ValueMember = value; } } } } [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Description("Currently selected value")] public object SelectedValue { get => _comboBox?.SelectedValue; set => SetSelectedValueSafe(value); } #endregion #region Public Properties - Appearance [Category("Appearance")] [Description("Header text displayed above the combo box")] [DefaultValue("")] public string HeaderText { get => _headerText; set { if (_headerText != value) { _headerText = value ?? string.Empty; if (_headerLabel != null) { _headerLabel.Text = _headerText; _headerLabel.Visible = !string.IsNullOrEmpty(_headerText); } } } } [Category("Appearance")] [Description("Font for items in the dropdown")] public Font ItemFont { get => _itemFont ?? (_itemFont = new Font("Tahoma", 9)); set { if (_itemFont != value && value != null) { _itemFont?.Dispose(); _itemFont = value; if (!DesignMode) { ApplyItemStyleSafe(); } } } } private bool ShouldSerializeItemFont() { return _itemFont != null && !_itemFont.Equals(new Font("Tahoma", 9)); } private void ResetItemFont() { ItemFont = new Font("Tahoma", 9); } [Category("Appearance")] [Description("Text color for items")] public Color ItemForeColor { get => _itemForeColor; set { if (_itemForeColor != value) { _itemForeColor = value; if (!DesignMode) { ApplyItemStyleSafe(); } } } } private bool ShouldSerializeItemForeColor() { return _itemForeColor != SystemColors.ControlText; } private void ResetItemForeColor() { ItemForeColor = SystemColors.ControlText; } [Category("Appearance")] [Description("Background color for items")] public Color ItemBackColor { get => _itemBackColor; set { if (_itemBackColor != value) { _itemBackColor = value; if (!DesignMode) { ApplyItemStyleSafe(); } } } } private bool ShouldSerializeItemBackColor() { return _itemBackColor != SystemColors.Window; } private void ResetItemBackColor() { ItemBackColor = SystemColors.Window; } [Category("Appearance")] [Description("Height of each row in pixels")] [DefaultValue(32)] public int ItemRowHeight { get => _itemRowHeight; set { if (_itemRowHeight != value && value > 0) { _itemRowHeight = value; if (!DesignMode && _comboBox?.EditorControl?.TableElement != null) { _comboBox.EditorControl.TableElement.RowHeight = value; } } } } [Category("Appearance")] [Description("Text alignment for items")] [DefaultValue(ContentAlignment.MiddleRight)] public ContentAlignment ItemTextAlign { get => _itemTextAlign; set { if (_itemTextAlign != value) { _itemTextAlign = value; if (!DesignMode) { ApplyItemStyleSafe(); } } } } [Category("Appearance")] [Description("Color used for highlighting matched text")] public Color HighlightColor { get => _highlightColor; set => _highlightColor = value; } private bool ShouldSerializeHighlightColor() { return _highlightColor != Color.FromArgb(201, 252, 254); } private void ResetHighlightColor() { HighlightColor = Color.FromArgb(201, 252, 254); } [Category("Appearance")] [Description("Placeholder text when no item is selected")] [DefaultValue("")] public string PlaceholderText { get => _comboBox?.NullText ?? string.Empty; set { if (_comboBox != null && !DesignMode) { _comboBox.NullText = value ?? string.Empty; } } } #endregion #region Public Properties - Behavior [Category("Behavior")] [DefaultValue(true)] [Description("Enable text highlighting during search")] public bool EnableHighlighting { get; set; } = true; [Browsable(false)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [Description("Indicates if dropdown is currently open")] public bool IsDropDownOpen => _comboBox?.MultiColumnComboBoxElement?.IsPopupOpen ?? false; [Category("Behavior")] [DefaultValue(300)] [Description("Height of the dropdown in pixels")] public int DropDownHeight { get => _comboBox?.MultiColumnComboBoxElement?.DropDownHeight ?? 300; set { if (_comboBox?.MultiColumnComboBoxElement != null && !DesignMode) { _comboBox.MultiColumnComboBoxElement.DropDownHeight = Math.Max(100, value); } } } #endregion #region Public Methods public void LoadData(IEnumerable data, string displayMember, string valueMember) { if (DesignMode) return; if (data == null) throw new ArgumentNullException(nameof(data)); if (string.IsNullOrEmpty(displayMember)) throw new ArgumentException("Display member cannot be null or empty", nameof(displayMember)); if (string.IsNullOrEmpty(valueMember)) throw new ArgumentException("Value member cannot be null or empty", nameof(valueMember)); try { _comboBox.SuspendLayout(); _comboBox.BeginInit(); DisplayMember = displayMember; ValueMember = valueMember; _comboBox.DataSource = data; ConfigureFiltering(); ApplyColumnConfigurations(); ApplyItemStyleSafe(); } catch (Exception ex) { throw new ComponentException($"Failed to load data: {ex.Message}", ex); } finally { _comboBox.EndInit(); _comboBox.ResumeLayout(true); } } public bool ShowDropDown() { if (DesignMode) return false; try { if (_comboBox?.MultiColumnComboBoxElement == null) return false; if (_comboBox.SelectedValue != null && !string.IsNullOrEmpty(ValueMember)) { ScrollToSelectedItem(); } _comboBox.MultiColumnComboBoxElement.ShowPopup(); return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ShowDropDown failed: {ex.Message}"); return false; } } public bool CloseDropDown() { if (DesignMode) return false; try { _comboBox?.MultiColumnComboBoxElement?.ClosePopup(); return true; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"CloseDropDown failed: {ex.Message}"); return false; } } public void ClearSelection() { if (DesignMode) return; try { _comboBox.SelectedValue = null; _comboBox.Text = string.Empty; _comboBox.MultiColumnComboBoxElement.TextBoxElement.TextBoxItem.SelectAll(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ClearSelection failed: {ex.Message}"); } } public void RefreshData() { if (DesignMode) return; try { _comboBox.EditorControl?.Refresh(); ApplyColumnConfigurations(); ApplyItemStyleSafe(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"RefreshData failed: {ex.Message}"); } } #endregion #region Private Methods - Setup private void CreateCustomControls() { if (_comboBox != null) return; // Already created _comboBox = new RadMultiColumnComboBox(); _headerLabel = new Label { Text = _headerText, Dock = DockStyle.Top, Height = 18, TextAlign = ContentAlignment.MiddleRight, Padding = new Padding(0, 0, 4, 0), Font = new Font(Font.FontFamily, Math.Max(6, Font.Size - 1), FontStyle.Bold), Visible = !string.IsNullOrEmpty(_headerText) }; this.Controls.Add(_comboBox); this.Controls.Add(_headerLabel); } private void SetupControlProperties() { try { this.Size = new Size(300, 50); this.MinimumSize = new Size(100, 30); SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true); _comboBox.Dock = DockStyle.Fill; _comboBox.RightToLeft = RightToLeft.Yes; _comboBox.DropDownStyle = RadDropDownStyle.DropDown; _comboBox.AutoFilter = true; var element = _comboBox.MultiColumnComboBoxElement; if (element != null) { element.DropDownAnimationEnabled = true; element.DropDownHeight = 300; } var editor = _comboBox.EditorControl; if (editor != null) { editor.ThemeName = "Fluent"; editor.TableElement.RowHeight = _itemRowHeight; editor.EnableSorting = false; editor.EnableGrouping = false; editor.MasterTemplate.ShowColumnHeaders = true; editor.MasterTemplate.AutoGenerateColumns = true; editor.MasterTemplate.EnableCustomFiltering = true; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"SetupControlProperties failed: {ex.Message}"); } } private void WireEvents() { try { if (_comboBox != null) { _comboBox.DropDownOpened += OnDropDownOpened; _comboBox.DropDownClosed += OnDropDownClosed; if (_comboBox.EditorControl != null) { _comboBox.EditorControl.KeyDown += EditorControl_KeyDown; _comboBox.EditorControl.CurrentRowChanged += EditorControl_CurrentRowChanged; _comboBox.EditorControl.CustomFiltering += EditorControl_CustomFiltering; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"WireEvents failed: {ex.Message}"); } } #endregion #region Private Methods - Data Management private void SetDataSourceSafe(object dataSource) { if (_comboBox == null || DesignMode) return; try { _comboBox.BeginInit(); _comboBox.DataSource = dataSource; if (dataSource != null) { ConfigureFiltering(); ApplyColumnConfigurations(); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"SetDataSource failed: {ex.Message}"); } finally { _comboBox.EndInit(); } } private void SetSelectedValueSafe(object value) { if (DesignMode || _comboBox?.SelectedValue?.Equals(value) == true) return; try { if (_comboBox != null) { _comboBox.SelectedValue = value; UpdateDisplayText(); OnSelectedValueChanged(EventArgs.Empty); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"SetSelectedValue failed: {ex.Message}"); } } private void ScrollToSelectedItem() { try { var editorControl = _comboBox.EditorControl; if (editorControl?.Rows == null || string.IsNullOrEmpty(ValueMember)) return; foreach (GridViewRowInfo row in editorControl.Rows) { if (row.Cells[ValueMember]?.Value?.Equals(_comboBox.SelectedValue) == true) { editorControl.CurrentRow = row; editorControl.TableElement.ScrollToRow(row); break; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ScrollToSelectedItem failed: {ex.Message}"); } } private void ConfigureFiltering() { try { if (string.IsNullOrEmpty(DisplayMember) || _comboBox?.EditorControl == null) return; var template = _comboBox.EditorControl.MasterTemplate; template.FilterDescriptors.Clear(); template.FilterDescriptors.Add(new FilterDescriptor { PropertyName = DisplayMember, Operator = FilterOperator.Contains }); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ConfigureFiltering failed: {ex.Message}"); } } #endregion #region Private Methods - Styling private void ApplyItemStyleSafe() { if (_isInitializing || DesignMode || _comboBox?.EditorControl == null) return; try { var grid = _comboBox.EditorControl; grid.Font = _itemFont; foreach (GridViewColumn column in grid.Columns) { column.HeaderTextAlignment = ContentAlignment.MiddleLeft; column.TextAlignment = _itemTextAlign; } foreach (GridViewRowInfo row in grid.Rows) { ApplyRowStyle(row); } grid.TableElement.RowHeight = _itemRowHeight; } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ApplyItemStyle failed: {ex.Message}"); } } private void ApplyRowStyle(GridViewRowInfo row) { try { foreach (GridViewCellInfo cell in row.Cells) { cell.Style.Font = _itemFont; cell.Style.ForeColor = _itemForeColor; cell.Style.BackColor = _itemBackColor; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ApplyRowStyle failed: {ex.Message}"); } } public void ApplyColumnConfigurations() { if (DesignMode) return; try { var grid = _comboBox?.EditorControl; if (grid?.Columns == null || _columnsConfig == null) return; foreach (var config in _columnsConfig) { if (string.IsNullOrEmpty(config.Name)) continue; var column = grid.Columns[config.Name]; if (column == null) continue; if (!string.IsNullOrEmpty(config.HeaderText)) { column.HeaderText = config.HeaderText; } column.HeaderTextAlignment = config.HeaderAlignment; column.TextAlignment = config.TextAlignment; if (config.Width > 0) { column.Width = config.Width; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ApplyColumnConfigurations failed: {ex.Message}"); } } private void UpdateDisplayText() { try { var row = _comboBox?.EditorControl?.CurrentRow; if (row != null && !string.IsNullOrEmpty(DisplayMember)) { _comboBox.Text = row.Cells[DisplayMember]?.Value?.ToString() ?? string.Empty; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"UpdateDisplayText failed: {ex.Message}"); } } #endregion #region Event Handlers private void OnDropDownOpened(object sender, EventArgs e) { _isDropDownOpen = true; DropDownOpened?.Invoke(this, e); } private void OnDropDownClosed(object sender, EventArgs e) { _isDropDownOpen = false; CommitSelection(); DropDownClosed?.Invoke(this, e); } private void EditorControl_KeyDown(object sender, KeyEventArgs e) { try { switch (e.KeyCode) { case Keys.Enter: CommitSelection(); CloseDropDown(); e.Handled = true; break; case Keys.Up: case Keys.Down: HandleArrowNavigation(e.KeyCode); e.Handled = true; break; case Keys.Escape: ClearSelection(); ClearHighlighting(); CloseDropDown(); e.Handled = true; break; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"KeyDown handler failed: {ex.Message}"); } } private void EditorControl_CurrentRowChanged(object sender, CurrentRowChangedEventArgs e) { try { if (!_isManualNavigation && e.CurrentRow != null) { _currentIndex = e.CurrentRow.Index; } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"CurrentRowChanged handler failed: {ex.Message}"); } } private void EditorControl_CustomFiltering(object sender, GridViewCustomFilteringEventArgs e) { if (!EnableHighlighting) return; try { string searchText = _comboBox?.Text ?? string.Empty; e.Visible = false; foreach (GridViewCellInfo cell in e.Row.Cells) { cell.Style.BackColor = Color.Empty; var cellText = cell.Value?.ToString() ?? string.Empty; if (cellText.IndexOf(searchText, StringComparison.OrdinalIgnoreCase) >= 0) { e.Visible = true; if (EnableHighlighting) { cell.Style.BackColor = HighlightColor; } } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"CustomFiltering handler failed: {ex.Message}"); } } #endregion #region Private Methods - Navigation & Selection private void HandleArrowNavigation(Keys key) { try { var grid = _comboBox?.EditorControl; if (grid?.RowCount == 0) return; int currentIdx = grid.CurrentRow?.Index ?? 0; int newIndex = key == Keys.Down ? Math.Min(currentIdx + 1, grid.RowCount - 1) : Math.Max(currentIdx - 1, 0); if (newIndex != currentIdx) { _isManualNavigation = true; grid.CurrentRow = grid.Rows[newIndex]; grid.TableElement.ScrollToRow(grid.CurrentRow); _isManualNavigation = false; ProvisionalSelectionChanged?.Invoke(this, EventArgs.Empty); } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"HandleArrowNavigation failed: {ex.Message}"); } } private void CommitSelection() { try { var currentRow = _comboBox?.EditorControl?.CurrentRow; if (currentRow != null && !string.IsNullOrEmpty(ValueMember)) { var newValue = currentRow.Cells[ValueMember]?.Value; if (!Equals(SelectedValue, newValue)) { SelectedValue = newValue; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"CommitSelection failed: {ex.Message}"); } } private void ClearHighlighting() { try { if (string.IsNullOrEmpty(DisplayMember)) return; foreach (GridViewRowInfo row in _comboBox?.EditorControl?.Rows ?? Enumerable.Empty()) { var cell = row.Cells[DisplayMember]; if (cell != null) { cell.Style.BackColor = Color.Empty; } } } catch (Exception ex) { System.Diagnostics.Debug.WriteLine($"ClearHighlighting failed: {ex.Message}"); } } #endregion #region Event Invocation protected virtual void OnSelectedValueChanged(EventArgs e) { SelectedValueChanged?.Invoke(this, e); } #endregion #region Dispose protected override void Dispose(bool disposing) { if (disposing) { // Dispose managed resources if needed // _itemFont?.Dispose(); // _comboBox?.Dispose(); // _headerLabel?.Dispose(); } base.Dispose(disposing); } #endregion } }