Search Combo Box in WiseJ

Answered
0
0

Hi All

This isn’t a question, but thought I would share a solution I have for creating a searchable WiseJ.Web.ComboBox. I have seen this asked once or twice, and would probably work for WinForms as well (untested). The idea is that you should be able to pass a class, define which fields to display and which to use as value/key. Some other minor features like limiting to the list and whether or not to populate the list on startup.

This is not all my own source, I have used part of Frank’s SearchTextBox (https://wisej.com/support/question/how-autocomplete-search-boxcontrol-from-httpwisej-comsupport-is-implemented) and Max Lambertini’s ideas (https://stackoverflow.com/questions/11780558/c-sharp-winforms-combobox-dynamic-autocomplete) to gather the ideas and create a control that is similar in functionality to the JQuery AutoComplete.

To use you will need to have a TextBox to capture the Id (this can be hidden) on your page. The Text box can just capture the same text as the ComboBox, but the idea is that you would generally use a KeyValuePair.

You simply drop the control where you need it and in your code you call the ComboboxName.Set()   when initialising the Combo, or alternatively the ComboboxName.MakeTheMagicHappen() if all the public variables are already set. This works best when using standard “DropDown” as the ComboBox DropDownStyle.

Hope this helps someone and that they can use. (See Answer for Full code – and excuse my coding style, I still have lots to learn)

Regards

Kevin Caine
253 Technologies and KLEVA

  • Kevin Caine
    Caveats: 1. Because this uses the text itself to find the ID to put into the TextBox, if you have 2 Matching text items in the ComboBox, only the first Item will be chosed for the ID field. 2. Dropping this from Toolbox sometimes gives a “Value cannot be null” error (but not always?) – this is probably my coding, but a work around is to create a ComboBox first, then edit the Designer code directly to change the ComboBox to your custom control. If someone can suggest what to change I would be grateful.
  • You must to post comments
Best Answer
0
0
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using Wisej.Web;

namespace MyApp.CustomControls {
    public class SearchComboBox : Wisej.Web.ComboBox {
        public int minLength { get; set; }
        public dynamic obj { get; set; }
        public string displayField { get; set; }
        public string valueField { get; set; }
        public bool limitToList { get; set; }
        public TextBox idTextBox { get; set; }
        public string defaultId { get; set; }
        public bool showInitialList { get; set; }
        public SearchComboBoxType searchType { get; set; }

        private string[] data { get; set; }
        private bool found { get; set; }

        public SearchComboBox() {
            this.KeyUp += SearchComboBox_KeyUp;
            this.TextChanged += SearchComboBox_TextChanged;
            this.Leave += SearchComboBox_Leave;
        }

        public void Set(dynamic ClassList, TextBox TextBoxForIdField, string DisplayItem, string IdItem = "", int MinimumLengthBeforeSearch = 2, bool LimitToList = true, bool ShowInitialList = true, string DefaultIdFieldValue = "0", SearchComboBoxType SearchComboBoxType = SearchComboBoxType.Contains){
            obj = ClassList;
            idTextBox = TextBoxForIdField;
            displayField = DisplayItem;
            if (IdItem == "") {
                valueField = DisplayItem;
            } else {
                valueField = IdItem;
            }
            minLength = MinimumLengthBeforeSearch;
            limitToList = LimitToList;
            defaultId = DefaultIdFieldValue;
            showInitialList = ShowInitialList;
            searchType = SearchComboBoxType;
            MakeTheMagicHappen();
        }

        public void MakeTheMagicHappen() {
            int cnt = obj.Count;
            data = new string[cnt];
            for (int i = 0; i < cnt; i++) {
                var changedItem = ToExpandoObject(obj[i]);
                var dictItem = changedItem as IDictionary<string, object>;
                data[i] = dictItem[displayField].ToString();
            }
            idTextBox.Text = defaultId;
            if (showInitialList) {
                ComboFill(obj, valueField, displayField);
            }
        }

        private void SearchComboBox_KeyUp(object sender, KeyEventArgs e) {
            if (e.KeyCode == Keys.Back) {
                int sStart = this.SelectionStart;
                if (sStart > 0) {
                    sStart--;
                    if (sStart == 0) {
                        this.Text = defaultId;
                    } else {
                        this.Text = this.Text.Substring(0, sStart);
                    }
                }
                e.Handled = true;
            }
        }

        private void SearchComboBox_TextChanged(object sender, EventArgs e) {
            HandleTextChanged();
            found = false;
            foreach (var o in obj) {
                if (o.Name == this.Text) {
                    idTextBox.Text = o.Id.ToString();
                    found = true;
                    break;
                }
            }
            if (!found) {
                idTextBox.Text = defaultId;
            }
        }

        private void HandleTextChanged() {
            var txt = this.Text;
            if (txt.Length > (minLength - 1)) {
                IEnumerable<string> list = null;
                switch (searchType) {
                    case SearchComboBoxType.Contains:
                        list = from d in data
                               where d.ToUpper().Contains(this.Text.ToUpper())
                               select d;
                        break;
                    case SearchComboBoxType.StartOnly:
                        list = from d in data
                               where d.ToUpper().StartsWith(this.Text.ToUpper())
                               select d;
                        break;
                    case SearchComboBoxType.EndOnly:
                        list = from d in data
                               where d.ToUpper().EndsWith(this.Text.ToUpper())
                               select d;
                        break;
                }
                if (list.Count() > 0) {
                    this.DataSource = list.ToList();
                    var sText = this.Items[0].ToString();
                    this.SelectionStart = txt.Length;
                    this.SelectionLength = sText.Length - txt.Length;
                    this.DroppedDown = true;
                    return;
                } else {
                    this.DroppedDown = false;
                    this.SelectionStart = txt.Length;
                }
            }
        }

        private void SearchComboBox_Leave(object sender, EventArgs e) {
            if (limitToList) {
                if (!found || idTextBox.Text == defaultId) {
                    this.Text = "";
                }
            }
        }

        // Helpers
        private ExpandoObject ToExpandoObject(object obj) {
            IDictionary<string, object> newObj = new ExpandoObject();
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj.GetType())) {
                newObj.Add(property.Name, property.GetValue(obj));
            }
            return (ExpandoObject)newObj;
        }

        private void ComboFill(IEnumerable<dynamic> dynObject, string valueField, string txtField = "") {
            List<KeyValuePair<string, string>> cboList = new List<KeyValuePair<string, string>>();
            if (dynObject != null && dynObject.Count() > 0) {
                cboList.Add(new KeyValuePair<string, string>(defaultId, ""));
                string dbFields = valueField;
                if (txtField != "") { dbFields += "," + txtField; }
                try {
                    foreach (dynamic item in dynObject) {
                        var changedItem = ToExpandoObject(item);
                        var dictItem = changedItem as IDictionary<string, object>;
                        string key = dictItem[valueField].ToString();
                        string value = key;
                        if (txtField != "") { value = dictItem[txtField].ToString(); }
                        cboList.Add(new KeyValuePair<string, string>(key, value));
                    }
                } catch {
                    // Well dammit - something didn't work!
                }
            } else {
                cboList.Add(new KeyValuePair<string, string>(valueField, txtField));
            }
            this.ValueMember = "key";
            this.DisplayMember = "value";
            this.DataSource = cboList;
        }

        public enum SearchComboBoxType {
            Contains = 0,
            StartOnly = 1,
            EndOnly = 2
        }
    }
}
  • You must to post comments
Showing 1 result
Your Answer

Please first to submit.