//------------------------------------------------------------------------------
// <copyright file=”*.cs” company=”EhLib Team”>
//     Copyright (c) 2017 Dmitry V. Bolshakov   
// </copyright>
//------------------------------------------------------------------------------

using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace EhLib
{

  /// <summary>
  /// Specify the type of filtering rows in ComboBoxEh
  /// </summary>
  public enum ComboBoxFilterType
  {
    /// <summary>
    /// Filtering by part of text that start a cell value 
    /// </summary>
    StartOfText,

    /// <summary>
    /// Filtering by part of text that is contained in the cell value 
    /// </summary>
    AnyPartOfText
  }

  /// <summary>
  /// Defines parts that compounds ComboBox
  /// </summary>
  public enum ComboBoxCompound
  {
    /// <summary>
    /// ComboBox consist of the text editor
    /// </summary>
    TextEditor,

    /// <summary>
    /// ComboBox consist of the text editor and a custom area
    /// </summary>
    CustomAreaAndTextEditor,

    /// <summary>
    /// ComboBox consist of a custom area. 
    /// CustomAreaPaint event handler should be writen to define display image of this part.
    /// </summary>
    CustomArea
  }

  public enum ListSourceOrigin
  {
    DataSource,
    Items
  }

  /// <summary>
  ///       Displays an editing field and a list, allowing the user to select from the
  ///       list or to enter new text. Displays only the editing field until the user
  ///       explicitly displays the list.
  /// </summary>
  [System.ComponentModel.DesignerCategory("Code")]
  [Designer("EhLib.Design.ComboBoxDesigner" + EhLibUtils.EhLibDesignDesignerVersionInfo)]
  [ToolboxItem(true)]
  [LookupBindingProperties("DataSource", "DisplayMember", "ValueMember", "SelectedValue")]
  [ClassInterface(ClassInterfaceType.AutoDispatch)]
  [ToolboxBitmap(typeof(ComboBoxEh), "ToolboxBitmaps.EhLib_ComboBox.bmp")]
  [Description("Displays an editable text box with a drop-down list of permitted values")]
  //[ComVisible(true)]
  public class ComboBoxEh : BaseTextBoxEh, IObjectCollectionObserver
  {
    #region private consts
    private static readonly object EventKeySelectedValueChanged = new object();
    private static readonly object EventKeySelectedIndexChanged = new object();
    private static readonly object EventKeyCustomAreaPaint = new object();
    private static readonly object EventKeyTextAreaPaint = new object();
    #endregion

    #region privates
    private object dataSource;
    private readonly CurrencyManager dataManager;
    private readonly bool dataManagerActive = false;
    private BindingMemberInfo displayMember;
    private BindingMemberInfo valueMember;
    private PropertyDescriptor displayMemberProperty;

    private bool inSetDataConnection;
    private bool isDataSourceInitEventHooked;
    private bool isDataSourceInitialized;
    private int selectedIndex = -1;
    private bool listVisible;
    private bool textAutoComplete = true;
    private bool textSearchIgnoreCase = true;
    private bool limitTextToListValuesStored;
    private bool limitTextToListValues;

    private readonly ComboBoxMessageFilter messageFilter;
    private readonly BindingSource bindingSource;

    private readonly PopupDataGridForm dropDownList;
    private readonly ComboBoxDropDownBox dropDownBox;
    private bool sorted;
    private ObjectCollection itemsCollection;
    private ComboBoxCompound compound;
    private int customAreaWidth = 16;
    private ComboBoxCustomAreaControl customAreaControl;
    #endregion privates

    #region constructor
    public ComboBoxEh()
    {
      WordWrap = false;

      dropDownList = new PopupDataGridForm(this);
      messageFilter = new ComboBoxMessageFilter(this);
      bindingSource = new BindingSource();
      dropDownBox = new ComboBoxDropDownBox(this);

      dataManager = bindingSource.CurrencyManager;
      dataManager.ListChanged += DataManager_ListChanged;

      InitData();

    }

    private void InitData()
    {
      InitEditButton();

      customAreaControl = new ComboBoxCustomAreaControl(this);
      customAreaControl.Parent = this;
    }

    protected override void Dispose(bool disposing)
    {
      CloseUp(false);
      if (disposing)
      {
        UnwireDataSource();
        dropDownList.Dispose();
        bindingSource.Dispose();
      }
      base.Dispose(disposing);
    }
    #endregion constructor

    #region design-time properties

    [DefaultValue(ComboBoxCompound.TextEditor)]
    public ComboBoxCompound Compound
    {
      get
      {
        return compound;
      }
      set
      {
        if (value != compound)
        {
          compound = value;
          OnCompoundChanged();
        }
      }
    }

    [Category("Data")]
    [DefaultValue(null)]
    [RefreshProperties(RefreshProperties.Repaint)]
    [AttributeProvider(typeof(IListSource))]
    //[SRDescription(SR.ListControlDataSourceDescr)
    public object DataSource
    {
      get
      {
        return dataSource;
      }

      set
      {
        if (value != null && !(value is IList || value is IListSource))
          throw new ArgumentException("BadDataSourceForComplexBinding");
        if (dataSource == value)
          return;
        try
        {
          SetDataConnection(value, displayMember, false);
        }
        catch(ArgumentException)
        {
          DisplayMember = "";
        }
        if (value == null)
          DisplayMember = "";
      }
    }

    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor", typeof(UITypeEditor))]
    [Category("Data")]
    [DefaultValue("")]
    [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter")]
    //[Description("ListControlDisplayMemberDescr")]
    public string DisplayMember
    {
      get
      {
        return displayMember.BindingMember;
      }

      set
      {
        if (value != displayMember.BindingPath)
        {
          BindingMemberInfo oldDisplayMember = displayMember;
          try
          {
            SetDataConnection(dataSource, new BindingMemberInfo(value), false);
          }
          catch (Exception e)
          {
            if (EhLibUtils.IsCriticalException(e))
            {
              throw;
            }
            displayMember = oldDisplayMember;
          }
        }
      }
    }

    [Category("Data")]
    [DefaultValue("")]
    [Editor("System.Windows.Forms.Design.DataMemberFieldEditor", typeof(UITypeEditor))]
    //[Description("ListControlValueMemberDescr")]
    public string ValueMember
    {
      get
      {
        return valueMember.BindingMember;
      }

      set
      {
        if (value == null)
          value = "";
        BindingMemberInfo newValueMember = new BindingMemberInfo(value);
        //BindingMemberInfo oldValueMember = valueMember;
        if (!newValueMember.Equals(valueMember))
        {
          if (DisplayMember.Length == 0)
            SetDataConnection(DataSource, newValueMember, false);
          if (isDataSourceInitialized && value.Length != 0)
            if (!BindingMemberInfoInDataManager(newValueMember))
            {
              throw new ArgumentException("ListControlWrongValueMember");
            }

          valueMember = newValueMember;
          //OnValueMemberChanged(EventArgs.Empty);
          //OnSelectedValueChanged(EventArgs.Empty);
        }
      }
    }

    [DefaultValue(true)]
    public bool TextAutoComplete
    {
      get
      {
        return textAutoComplete;
      }

      set
      {
        textAutoComplete = value;
      }
    }

    [DefaultValue(true)]
    public bool TextSearchIgnoreCase
    {
      get
      {
        return textSearchIgnoreCase;
      }

      set
      {
        textSearchIgnoreCase = value;
      }
    }

    [DefaultValue(true)]
    public bool LimitTextToListValues
    {
      get
      {
        if (limitTextToListValuesStored)
          return limitTextToListValues;
        else
          return DefaultLimitTextToListValues();
      }
      set
      {
        if ((limitTextToListValuesStored == false) || (limitTextToListValues != value))
        {
          limitTextToListValuesStored = true;
          limitTextToListValues = value;
          LimitTextToListValuesChanged();
        }
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ComboBoxDropDownBox DropDownBox
    {
      get
      {
        return dropDownBox;
      }
    }

    [DefaultValue(false)]
    public bool Sorted
    {
      get
      {
        return sorted;
      }
      set
      {
        if (sorted != value)
        {
          if (this.DataSource != null && value)
          {
            throw new ArgumentException("ComboBoxSortWithDataSource");
          }

          sorted = value;
          Items.Sorted = value;
          //RefreshItems();
          SelectedIndex = -1;
        }
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    [Localizable(true)]
    [Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor", typeof(UITypeEditor))]
    [MergableProperty(false)]
    [Category("Data")]
    //[Description("ComboBoxItemsDescr")]
    public ObjectCollection Items
    {
      get
      {
        if (itemsCollection == null)
        {
          itemsCollection = new ObjectCollection(this);
        }
        return itemsCollection;
      }
    }

    //[DefaultValue(HorizontalAlignment.Left)]
    //[Localizable(true)]
    //public new HorizontalAlignment TextAlign
    //{
    //  get { return base.TextAlign; }
    //  set { base.TextAlign = value; }
    //}

    [DefaultValue(true)]
    public new bool HideSelection
    {
      get { return base.HideSelection; }
      set { base.HideSelection = value; }
    }

    [DefaultValue(false)]
    [Localizable(true)]
    public new bool WordWrap
    {
      get
      {
        return base.WordWrap;
      }

      set
      {
        if (value != WordWrap)
        {
          base.WordWrap = value;
          Multiline = WordWrap;
        }
      }
    }

    [DefaultValue(16)]
    public int CustomAreaWidth
    {
      get
      {
        return customAreaWidth;
      }

      set
      {
        if (value != customAreaWidth)
        {
          customAreaWidth = value;
          RealignControls();
        }
      }
    }
    #endregion

    #region run-time properties     
    [Browsable(false)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    public int SelectedIndex
    {
      get
      {
        return selectedIndex;
      }
      set
      {
        if (value != selectedIndex)
        {
          CheckSelectedIndex(value);
          selectedIndex = value;
          if (listVisible)
            dropDownList.SelectedIndex = value;
          UpdateTextFromIndex();
          SelectAll();
          customAreaControl.Invalidate();
          OnSelectedIndexChanged(EventArgs.Empty);
        }
      }
    }

    [Category("Data")]
    [Browsable(false)]
    [Bindable(true)]
    [DefaultValue(null)]
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    //[Description("ListControlSelectedValueDescr")]
    public object SelectedValue
    {
      get
      {
        if (SelectedIndex != -1 && dataManager != null)
        {
          object currentItem = dataManager.List[SelectedIndex];
          object filteredItem = FilterItemOnProperty(currentItem, valueMember.BindingField);
          return filteredItem;
        }
        return null;
      }
      set
      {
        if (dataManager != null)
        {
          string propertyName = valueMember.BindingField;
          if (string.IsNullOrEmpty(propertyName))
            throw new InvalidOperationException("ListControlEmptyValueMemberInSettingSelectedValue");
          PropertyDescriptorCollection props = dataManager.GetItemProperties();
          PropertyDescriptor property = props.Find(propertyName, true);
          int index = FindInDataManager(property, value, true);
          this.SelectedIndex = index;
        }
      }
    }

    [Browsable(false)]
    public bool LookupMode
    {
      get
      {
        return !string.IsNullOrEmpty(ValueMember);
      }
    }

    public bool ListVisible
    {
      get
      {
        return listVisible;
      }
    }

    [Browsable(false)]
    protected CurrencyManager DataManager
    {
      get
      {
        return this.dataManager;
      }
    }

    public ListSourceOrigin ListSourceOrigin
    {
      get
      {
        if (DataSource == null && Items.Count > 0)
          return ListSourceOrigin.Items;
        else
          return ListSourceOrigin.DataSource;
      }
    }
    #endregion

    #region events
    public event EventHandler SelectedValueChanged
    {
      add
      {
        base.Events.AddHandler(EventKeySelectedValueChanged, value);
      }
      remove
      {
        base.Events.RemoveHandler(EventKeySelectedValueChanged, value);
      }
    }

    public event EventHandler SelectedIndexChanged
    {
      add
      {
        Events.AddHandler(EventKeySelectedIndexChanged, value);
      }
      remove
      {
        Events.RemoveHandler(EventKeySelectedIndexChanged, value);
      }
    }

    public event EventHandler<ComboBoxCustomAreaPaintEventArgs> CustomAreaPaint
    {
      add
      {
        Events.AddHandler(EventKeyCustomAreaPaint, value);
      }
      remove
      {
        Events.RemoveHandler(EventKeyCustomAreaPaint, value);
      }
    }

    public event EventHandler<ComboBoxTextAreaPaintEventArgs> TextAreaPaint
    {
      add
      {
        Events.AddHandler(EventKeyTextAreaPaint, value);
      }
      remove
      {
        Events.RemoveHandler(EventKeyTextAreaPaint, value);
      }
    }
    #endregion events

    #region methods
    protected override TextBoxBase CreateTextEditControl()
    {
      return new ComboBoxTextEditControl(this);
    }

    protected PropertyDescriptor GetMemberProperty(string memberName)
    {
      if (dataManager != null)
      {
        PropertyDescriptorCollection propCol = dataManager.GetItemProperties();
        return propCol.Find(memberName, true);
      } else
      {
        return null;
      }
    }

    protected int FindInDataManager(PropertyDescriptor property, object key, bool keepIndex)
    {
      if (key == null)
      {
        throw new ArgumentNullException("key");
      }
      if (((property != null) && (dataManager.List is IBindingList)) && ((IBindingList)dataManager.List).SupportsSearching)
      {
        return ((IBindingList)dataManager.List).Find(property, key);
      }
      for (int i = 0; i < this.dataManager.List.Count; i++)
      {
        Debug.Assert(property != null, @"property != null");
        object obj2 = property.GetValue(dataManager.List[i]);
        if (key.Equals(obj2))
        {
          return i;
        }
      }
      return -1;
    }

    protected object FilterItemOnProperty(object item)
    {
      return FilterItemOnProperty(item, displayMember.BindingField);
    }

    protected object FilterItemOnProperty(object item, string field)
    {
      object result = item;

      if (item != null && field.Length > 0)
      {
        //try
        //{

        // if we have a dataSource, then use that to display the string
        PropertyDescriptor descriptor;
        if (this.dataManager != null)
          descriptor = this.dataManager.GetItemProperties().Find(field, true);
        else
          descriptor = TypeDescriptor.GetProperties(item).Find(field, true);
        if (descriptor != null)
        {
          result = descriptor.GetValue(item);
        }

        //}
        //catch
        //{
        //}
      }

      return result;
    }

    private bool BindingMemberInfoInDataManager(BindingMemberInfo bindingMemberInfo)
    {
      if (dataManager == null)
        return false;
      PropertyDescriptorCollection props = dataManager.GetItemProperties();
      int propsCount = props.Count;

      for (int i = 0; i < propsCount; i++)
      {
        if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
          continue;
        if (props[i].Name.Equals(bindingMemberInfo.BindingField))
        {
          return true;
        }
      }

      for (int i = 0; i < propsCount; i++)
      {
        if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
          continue;
        if (string.Compare(props[i].Name, bindingMemberInfo.BindingField, true, CultureInfo.CurrentCulture) == 0)
        {
          return true;
        }
      }

      return false;
    }

    //private void DataManager_PositionChanged(object sender, EventArgs e)
    //{
    //}

    protected virtual void DataManager_ItemChanged(object sender, ItemChangedEventArgs e)
    {
    }

    private void DataManager_ListChanged(object sender, ListChangedEventArgs e)
    {
      bool newDataManagerActive = dataManager.GetItemProperties().Count > 0;
      if (dataManagerActive != newDataManagerActive)
      {
        if (dataManagerActive)
        {

        }
      }
    }

    private void DataSourceDisposed(object sender, EventArgs e)
    {
      Debug.Assert(sender == this.dataSource, "dispose notification for other than our dataSource?");
      SetDataConnection(null, new BindingMemberInfo(""), true);
    }

    private void DataSourceInitialized(object sender, EventArgs e)
    {
      ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);
      Debug.Assert(dsInit != null, "ListControl: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!");
      Debug.Assert(dsInit.IsInitialized, "ListControl: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing.");
      SetDataConnection(this.dataSource, this.displayMember, true);
    }

    private void SetDataConnection(object newDataSource, BindingMemberInfo newDisplayMember, bool force)
    {
      bool dataSourceChanged = (dataSource != newDataSource);
      bool displayMemberChanged = (!displayMember.Equals(newDisplayMember));

      if (inSetDataConnection)
        return;

      try
      {
        if (force || dataSourceChanged || displayMemberChanged)
        {
          inSetDataConnection = true;
          //IList currentList = this.DataManager != null ? this.DataManager.List : null;
          //bool currentManagerIsNull = this.DataManager == null;

          UnwireDataSource();

          dataSource = newDataSource;
          displayMember = newDisplayMember;

          WireDataSource();

          if (isDataSourceInitialized)
          {
            bindingSource.DataSource = DataSource;
            bindingSource.DataMember = displayMember.BindingPath;

            if (dataManager != null && 
                (displayMemberChanged || dataSourceChanged) && 
                !string.IsNullOrEmpty(displayMember.BindingMember))
            {
              //if (!BindingMemberInfoInDataManager(displayMember))
              //  throw new ArgumentException("ListControlWrongDisplayMember");
            }

            if (dataManager != null)
            {
              if (displayMemberChanged)
              {
                DataManager_ItemChanged(dataManager, null);
              }
            }
          }
          //this.displayMemberConverter = null;
        }

        displayMemberProperty = GetMemberProperty(displayMember.BindingField);

        //if (dataSourceChanged)
        //{
        //  OnDataSourceChanged(EventArgs.Empty);
        //}

        //if (displayMemberChanged)
        //{
        //  OnDisplayMemberChanged(EventArgs.Empty);
        //}
      }
      finally
      {
        inSetDataConnection = false;
      }
    }

    private void UnwireDataSource()
    {
      var source = this.dataSource as IComponent;
      if (source != null)
      {
        source.Disposed -= DataSourceDisposed;
      }

      ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);

      if (dsInit != null && isDataSourceInitEventHooked)
      {
        dsInit.Initialized -= DataSourceInitialized;
        isDataSourceInitEventHooked = false;
      }
    }

    private void WireDataSource()
    {
      var source = this.dataSource as IComponent;
      if (source != null)
      {
        source.Disposed += DataSourceDisposed;
      }

      ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);

      if (dsInit != null && !dsInit.IsInitialized)
      {
        dsInit.Initialized += DataSourceInitialized;
        isDataSourceInitEventHooked = true;
        isDataSourceInitialized = false;
      }
      else if (this.dataSource != null)
      {
        isDataSourceInitialized = true;
      }
    }

    private void UpdateTextFromIndex()
    {
      string newText = "";

      if (dataManager != null && SelectedIndex >= 0 && SelectedIndex < dataManager.Count)
      {
        object rowItem = dataManager.List[SelectedIndex];
        object valItem = FilterItemOnProperty(rowItem);
        if (valItem == null)
          newText = "";
        else
          newText = valItem.ToString();
      }

      Text = newText;
    }

    private void UpdateSelectedIndexFromText()
    {
      int index = FindStringInList(Text, false, false);
      InternalSetSelectedIndex(index);
    }

    private void AppMouseDown(ref Message m)
    {
      Control ctrl = FromHandle(m.HWnd);
      if (!(ctrl == this || Contains(ctrl) || ctrl == dropDownList || dropDownList.Contains(ctrl)))
        CloseUp(false);
    }

    protected override void OnGotFocus(EventArgs e)
    {
      base.OnGotFocus(e);
      SelectAll();
    }

    protected override void OnLostFocus(EventArgs e)
    {
      base.OnLostFocus(e);
      if (!EhLibManager.DefaultEhLibManager.DropDownDebug)
        CloseUp(false);
    }

    protected override void OnKeyDown(KeyEventArgs e)
    {
      base.OnKeyDown(e);

      if (listVisible)
      {
        dropDownList.ProcessKeyDown(e);
        if (e.Handled && !ReadOnly)
          SelectedIndex = dropDownList.SelectedIndex;
      }

      if (!e.Handled)
      {
        switch (e.KeyCode)
        {
          case Keys.Escape:
            if (listVisible)
            {
              CloseUp(false);
              e.Handled = true;
            }
            break;

          case Keys.Delete:
            if (LimitTextToListValues && !ReadOnly)
            {
              SelectedIndex = -1;
              e.Handled = true;
            }
            break;

          case Keys.Up:
            e.Handled = SelectPriorValue();
            break;

          case Keys.Down:
            if (e.Alt && !e.Shift && !e.Control)
            {
              if (listVisible)
                CloseUp(false);
              else
                DropDown();
              e.Handled = true;
            }
            else
              e.Handled = SelectNextValue();
            break;

          case Keys.Return:
            if (!Multiline)
              e.Handled = true;
            break;
        }
      }

      if (e.Handled)
      {
        e.SuppressKeyPress = true;
      }

    }

    internal int Compare(object item1, object item2)
    {
      throw new NotSupportedException("NotImplemented");
    }

    protected override void OnKeyPress(KeyPressEventArgs e)
    {
      switch (e.KeyChar)
      {
        case (char)Keys.Back:
          if (LimitTextToListValues && !ReadOnly)
          {
            ProcessSearchStr(e.KeyChar.ToString());
            e.Handled = true;
          }
          break;
        default:
          if (e.KeyChar >= 32)
          {
            if (DropDownBox.AutoDrop && !listVisible)
              DropDown();
            if (LimitTextToListValues && !ReadOnly)
            {
              ProcessSearchStr(e.KeyChar.ToString());
              e.Handled = true;
            }
          }
          break;
      }
    }

    protected override void OnMouseWheel(MouseEventArgs e)
    {
      base.OnMouseWheel(e);
      HandledMouseEventArgs hme = (HandledMouseEventArgs)e;
      //if (hme != null)
      //{
      if (hme.Handled)
      {
        return;
      }
      hme.Handled = true;
      //}

      if ((ModifierKeys & (Keys.Shift | Keys.Alt)) != 0 || MouseButtons != MouseButtons.None)
      {
        return; // Do not scroll when Shift or Alt key is down, or when a mouse button is down.
      }

      if (e.Delta > 0)
        hme.Handled = SelectPriorValue();
      else if (e.Delta < 0)
        hme.Handled = SelectNextValue();
    }

    protected override void OnTextChanged(EventArgs e)
    {
      if (UserTextChanged)
        UpdateSelectedIndexFromText();          
      base.OnTextChanged(e);
    }

    protected internal override void TextBoxControl_WndProc(ref Message msg, ref bool handled)
    {
      switch (msg.Msg)
      {
        case NativeMethods.WM_CUT:
          WmCut(ref handled);
          break;

        case NativeMethods.WM_PASTE:
          WmPaste(ref handled);
          break;

        case NativeMethods.WM_CLEAR:
          WmClear(ref handled);
          break;
      }

      base.TextBoxControl_WndProc(ref msg, ref handled);
    }

    private void WmCut(ref bool handled)
    {
      if (LimitTextToListValues)
      {
        Copy();
        handled = true;
      }
    }

    private void WmPaste(ref bool handled)
    {
      if (LimitTextToListValues && Clipboard.ContainsText())
      {
        ProcessSearchStr(Clipboard.GetText());
        handled = true;
      }
    }

    private void WmClear(ref bool handled)
    {
      if (LimitTextToListValues && !ReadOnly)
      {
        SelectedIndex = -1;
        handled = true;
      }
    }

    protected internal override void OnKeyPressProcessed(KeyPressEventArgs e)
    {
      int oldSelStart;
      if (!LimitTextToListValues &&
          e.KeyChar != (char)Keys.Delete)
      //     not(ssCtrl in KeyDataToShiftState(Message.KeyData))
      {
        if (!(SelectionStart == Text.Length && SelectionLength == 0) ||
              (e.KeyChar == (char)Keys.Back))
        {
          oldSelStart = SelectionStart;
          //GetItemsList;
          if (LocateStr(Text, false))
          {
            Select(Text.Length, oldSelStart - Text.Length);
            //SelectionStart = Text.Length;
            //SelectionLength = oldSelStart - SelectionStart;
          }

          if (DropDownBox.AutoFilter && listVisible)
          {
            string filterText = Text.Substring(0, SelectionStart);
            dropDownList.FilterText = filterText;
            //dropDownList.SelectedIndex = DropDownBox.;
          }
        }
        else
        {
          ProcessSearchStr("");
        }
      }
    }

    protected override bool ProcessKeyEventArgs(ref Message m)
    {
      //int oldSelStart;
      //char charCode = unchecked((char)(long)m.WParam);
      bool result = base.ProcessKeyEventArgs(ref m);

      //if ( (m.Msg == NativeMethods.WM_CHAR) &&
      //   //          !LimitTextToListValues and
      //    !(charCode == (char)Keys.Delete))
      ////     not(ssCtrl in KeyDataToShiftState(Message.KeyData))
      //{
      //  if (!(SelectionStart == Text.Length && SelectionLength = 0) ||
      //        (charCode == (char)Keys.Back))
      //  {
      //    oldSelStart = SelectionStart;
      //    //GetItemsList;
      //    if (LocateStr(Text, false))
      //    {
      //      SelectionStart = Text.Length;
      //      SelectionLength = oldSelStart - SelectionStart;
      //    }
      //  }
      //  else
      //  {
      //    ProcessSearchStr("");
      //  }
      //}
      return result;
    }
    //protected internal override bool ProcessKeyMessage(ref Message m)
    //{
    //  return base.ProcessKeyEventArgs(ref m);
    //}

    protected override bool IsInputKey(Keys keyData)
    {
      switch (keyData & Keys.KeyCode)
      {
        case Keys.Return:
          if (listVisible) return true;
          break;
      }
      return base.IsInputKey(keyData);
    }

    protected virtual void OnSelectedIndexChanged(EventArgs e)
    {
      var handler = Events[EventKeySelectedIndexChanged] as EventHandler;
      if (handler != null)
      {
        handler(this, e);
      }

      OnSelectedValueChanged(e);
    }

    protected virtual void OnSelectedValueChanged(EventArgs e)
    {
      var handler = Events[EventKeySelectedValueChanged] as EventHandler;
      if (handler != null)
      {
        handler(this, e);
      }
    }

    protected virtual void LimitTextToListValuesChanged()
    {
    }

    private bool DefaultLimitTextToListValues()
    {
      return !string.IsNullOrEmpty(ValueMember);
    }

    protected virtual bool ShouldSerializeLimitTextToListValues()
    {
      return (limitTextToListValuesStored == true);
    }

    protected int InternalFindStringInList(string str, bool ignoreCase, bool partialKey)
    {
      string valAsStr;
      int result = -1;
      //int OldSelectedIndex = SelectedIndex;

      if (dataManager != null)
      {
        for (int i = 0; i < dataManager.List.Count; i++)
        {
          object value = GetItemText(dataManager.List[i]);

          if (value != null)
          {
            valAsStr = value.ToString();

            if (EhLibUtils.StringEquals(str, valAsStr, ignoreCase, partialKey))
            {
              result = i;
              break;
            }
          }
        }
      }
      return result;
    }

    private void CheckNoDataSource(string errorText)
    {
      if (DataSource != null)
      {
        throw new ArgumentException(errorText);
      }
    }

    //private void RefreshItems()
    //{
    //  ObjectCollection savedItems = itemsCollection;
    //  object[] newItems;
    //  newItems = new object[savedItems.Count];
    //  savedItems.CopyTo(newItems, 0);
    //}

    private void ResetItemsConnection()
    {
      if (DataSource == null)
      {
        if (Items.Count > 0)
        {
          bindingSource.DataSource = Items;
          bindingSource.DataMember = "";
        }
        else
        {
          bindingSource.DataSource = null;
          bindingSource.DataMember = "";
        }
      }
    }

    private void ItemsChanged()
    {
      ResetItemsConnection();
    }

    protected internal override bool IsInEditControlDefaultVisible(EditItem inEditControl)
    {
      return true;
    }

    protected override bool EditButtonDefaultVisible()
    {
      return true;
    }

    protected virtual void OnCompoundChanged()
    {
      RealignControls();
    }

    protected override void SetEditControlBounds(Rectangle editControlAreaBound)
    {
      if (customAreaControl != null)
      {
        if (Compound == ComboBoxCompound.CustomAreaAndTextEditor)
        {
          customAreaControl.Visible = true;
          customAreaControl.Bounds = new Rectangle(editControlAreaBound.Location,
                                                   new Size(CustomAreaWidth, editControlAreaBound.Height));
          customAreaControl.Visible = true;
          editControlAreaBound.X += CustomAreaWidth;
          editControlAreaBound.Width -= CustomAreaWidth;
        }
        else
        {
          customAreaControl.Visible = false;
          customAreaControl.Bounds = Rectangle.Empty;
        }
      }
      base.SetEditControlBounds(editControlAreaBound);
    }

    internal protected virtual void OnCustomAreaPaint(ComboBoxCustomAreaPaintEventArgs e)
    {
      var handler = Events[EventKeyCustomAreaPaint] as EventHandler<ComboBoxCustomAreaPaintEventArgs>;
      if (handler != null)
      {
        handler(this, e);
      }
      if (!e.Handled)
      {
        using (Pen pen = new Pen(ForeColor))
        {
          Rectangle drawRect = e.AreaRect;
          e.Graphics.FillRectangle(new SolidBrush(BackColor), e.AreaRect);
          drawRect.Inflate(-1, -1);
          Rectangle frameRect = drawRect;
          frameRect.Width = frameRect.Width - 1;
          frameRect.Height = frameRect.Height - 1;
          e.Graphics.DrawRectangle(pen, frameRect);
          e.Graphics.DrawLine(pen, frameRect.Location, new Point(frameRect.Right, frameRect.Bottom));
          e.Graphics.DrawLine(pen, new Point(frameRect.Left, frameRect.Bottom), new Point(frameRect.Right, frameRect.Top));
        }
      }
    }

    internal protected virtual void OnTextAreaPaint(ComboBoxTextAreaPaintEventArgs e)
    {
      var handler = Events[EventKeyTextAreaPaint] as EventHandler<ComboBoxTextAreaPaintEventArgs>;
      if (handler != null)
      {
        handler(this, e);
      }
      if (!e.Handled)
      {
        e.DrawContent(e);
      }
    }
    #endregion methods

    #region public methos
    public string GetItemText(object item)
    {

      object itemValue;

      if (item == null)
      {
        return string.Empty;
      }

      if (displayMemberProperty != null)
        itemValue = displayMemberProperty.GetValue(item);
      else
        itemValue = item;

      if (itemValue != null)
        return Convert.ToString(itemValue, CultureInfo.CurrentCulture);
      else
        return "";
    }

    public object GetValueByIndex(int index)
    {
      if (index != -1 && dataManager != null)
      {
        object currentItem = dataManager.List[index];
        object filteredItem = FilterItemOnProperty(currentItem, valueMember.BindingField);
        return filteredItem;
      }
      else
      {
        return null;
      }
    }

    public override void InEditButtonDownDefaultAction(EditItem inEditControl, DownEventArgs e)
    {
      //NumericBoxEhManager.DefaultManager.InEditButtonDownDefaultAction(this, inEditControl, e);
      //dropDownList.CreateControl();
      if (listVisible)
        CloseUp(false);
      else
        DropDown();
      e.Handled = true;
      //EhLibUtils.SendMessage(dropDownList.dataGrid, NativeMethods.WM_SETFOCUS, (IntPtr)0, (IntPtr)0);
    }

    public void DropDown()
    {
      if (listVisible) return;
      if (ReadOnly && !EditButton.Visible) return;

      dropDownList.DataSource = bindingSource;
      //dropDownList.dataGrid.DataMember = displayMember.BindingPath;
      dropDownList.DisplayMember = displayMember.BindingField;
      dropDownList.SelectedIndex = SelectedIndex;

      dropDownList.Grid.Font = DropDownBox.Font;
      dropDownList.Grid.BackColor = DropDownBox.BackColor;
      dropDownList.Grid.ForeColor = DropDownBox.ForeColor;

      dropDownList.Grid.RowOptions.HeightOptions.AutoExpand = DropDownBox.RowOptions.HeightOptions.AutoExpand;
      dropDownList.Grid.RowOptions.HeightOptions.ContentHeight = DropDownBox.RowOptions.HeightOptions.ContentHeight;
      dropDownList.Grid.RowOptions.HeightOptions.MaxContentHeight = DropDownBox.RowOptions.HeightOptions.MaxContentHeight;
      dropDownList.Grid.RowOptions.HeightOptions.Unit = DropDownBox.RowOptions.HeightOptions.Unit;

      dropDownList.Grid.RowOptions.HorzLine.Visible = DropDownBox.RowOptions.HorzLine.Visible;
      dropDownList.Grid.RowOptions.HorzLine.Color = DropDownBox.RowOptions.HorzLine.Color;
      dropDownList.Grid.RowOptions.HorzLine.Style = DropDownBox.RowOptions.HorzLine.Style;

      dropDownList.Grid.AutoGenerateColumns = true;
      dropDownList.FilterType = DropDownBox.FilterType;
      dropDownList.VisibleRowCount = DropDownBox.HeightInRows;
      dropDownList.UpdateHeight(true);

      if (DropDownBox.Width != 0)
        dropDownList.Width = DropDownBox.Width;
      else
        dropDownList.Width = Width;

      VerticalDropLayout layout = 
        EhLibUtils.AlignDropDownWindowRect(RectangleToScreen(ClientRectangle), dropDownList, DropDownAlign.Left);
      if (layout == VerticalDropLayout.AboveControl)
        dropDownList.SizeGripPosition = SizeGripPosition.TopRight;
      else
        dropDownList.SizeGripPosition = SizeGripPosition.BottomRight;

      dropDownList.ResetOldSizeInfo();
      dropDownList.Show();
      listVisible = true;
      Application.AddMessageFilter(messageFilter);
    }

    public void CloseUp(bool acceptValue)
    {
      if (!listVisible) return;

      Application.RemoveMessageFilter(messageFilter);
      dropDownList.Hide();
      listVisible = false;

      if (dropDownList.WidthChanged)
        DropDownBox.Width = dropDownList.Width;

      if (dropDownList.HeightChanged)
        DropDownBox.HeightInRows = dropDownList.VisibleRowCount;

      if (acceptValue && !ReadOnly)
      {
        //Text = dropDownList.SelectedValue.ToString();
        SelectedIndex = dropDownList.SelectedIndex;
      }

      dropDownList.FilterText = "";
      dropDownList.DataSource = null;
      dropDownList.DisplayMember = null;
    }

    public int FindListDisplayText(bool partialSearch, bool ignoreCase)
    {
      return -1;
    }

    public bool ProcessSearchStr(string str)
    {
      int oldSelLenght;
      bool result = false;

      if (str.Length == 1 && str[0] == (char)Keys.Back)
      {
        if (Text.Length == SelectionLength)
        {
          Select(Text.Length, -1);
          //SelectionStart = int.MaxValue;
          //SelectionLength = -1;
        }
        else
        {
          oldSelLenght = Math.Abs(SelectionLength);
          Select(Text.Length, -oldSelLenght - 1);
          //SelectionStart = int.MaxValue;
          //SelectionLength = -oldSelLenght - 1;
        }
      }
      else
      {
        string s;
        string searchText = "";

        if (Text.Length > 0)
          searchText = Text.Substring(0, SelectionStart);
        s = searchText + str;
        if (!String.IsNullOrEmpty(s))
          result = LocateStr(s, true);
      }

      if (DropDownBox.AutoFilter && listVisible)
      {
        string filterText = Text.Substring(0, SelectionStart);
        dropDownList.FilterText = filterText;
        //dropDownList.SelectedIndex = DropDownBox.;
      }

      return result;
    }

    public bool LocateStr(string str, bool partialKey)
    {
      int index;
      int oldSelectedIndex = SelectedIndex;

      index = FindStringInList(str, partialKey, TextSearchIgnoreCase);

      if (index >= 0)
      {
        //DataManager.Position = 
        //SelectedIndex = index;
        InternalSetSelectedIndex(index);
        UpdateTextFromIndex();
        Select(Text.Length, str.Length - Text.Length);
        ScrollToCaret();
      }
      else if (!LimitTextToListValues)
      {
        InternalSetSelectedIndex(-1);
      }

      if (oldSelectedIndex != SelectedIndex)
        return true;
      else
        return false;
    }

    public int FindStringInList(string str, bool partialKey, bool ignoreCase)
    {
      int result = InternalFindStringInList(str, false, false);

      if ((result == -1) && ignoreCase)
        result = InternalFindStringInList(str, true, false);

      if ((result == -1) && partialKey)
      {
        result = InternalFindStringInList(str, false, true);

        if ((result == -1) && ignoreCase)
          result = InternalFindStringInList(str, true, true);
      }

      return result;
    }

    void InternalSetSelectedIndex(int value)
    {
      if (selectedIndex != value)
      {
        CheckSelectedIndex(value);
        selectedIndex = value;
        if (listVisible)
          dropDownList.SelectedIndex = value;
        customAreaControl.Invalidate();
        OnSelectedIndexChanged(EventArgs.Empty);
        //Debug.WriteLine("selectedIndex:" + selectedIndex.ToString());
      }
    }

    private void CheckSelectedIndex(int value)
    {
      int itemCount = 0;

      if (dataManager != null)
        itemCount = dataManager.Count;

      if (value < -1 || value >= itemCount)
      {
        throw new ArgumentOutOfRangeException("SelectedIndex: " + value.ToString() +  " is invalid");
      }
    }

    private bool SelectNextValue()
    {
      if (dataManager != null && SelectedIndex < dataManager.Count - 1 && !ReadOnly)
      {
        SelectedIndex = SelectedIndex + 1;
        return true;
      }
      else
      {
        return false;
      }
    }

    private bool SelectPriorValue()
    {
      if (dataManager != null && SelectedIndex > 0 && !ReadOnly)
      {
        SelectedIndex = SelectedIndex - 1;
        return true;
      }
      else
      {
        return false;
      }
    }

    public virtual void ResetLimitTextToListValues()
    {
      limitTextToListValuesStored = false;
      LimitTextToListValuesChanged();
    }

    void IObjectCollectionObserver.OnCollectionChangeAction(ObjectCollection collection, CollectionExtendedChangeAction action, int index, object oldItem, object newItem)
    {
      if (action == CollectionExtendedChangeAction.CollectionChanging ||
          action == CollectionExtendedChangeAction.ItemAdding ||
          action == CollectionExtendedChangeAction.ItemChanging ||
          action == CollectionExtendedChangeAction.ItemRemoving)
      {
        CheckNoDataSource("Attempt to change ComboBox.Items when the DataSource != null.");
      }
      else
      {
        ItemsChanged();
      }
    }

    int IObjectCollectionObserver.OnCompareCollectionItems(object item1, object item2)
    {
      string itemName1 = GetItemText(item1);
      string itemName2 = GetItemText(item2);

      CompareInfo compInfo = (Application.CurrentCulture).CompareInfo;
      return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort);
    }

    #endregion

    #region internal classes
    //private class ItemComparer : IComparer
    //{
    //  private ComboBoxEh comboBox;

    //  public ItemComparer(ComboBoxEh comboBox)
    //  {
    //    this.comboBox = comboBox;
    //  }

    //  public int Compare(object item1, object item2)
    //  {
    //    if (item1 == null)
    //    {
    //      if (item2 == null)
    //        return 0; //both null, then they are equal

    //      return -1; //item1 is null, but item2 is valid (greater)
    //    }
    //    if (item2 == null)
    //      return 1; //item2 is null, so item 1 is greater

    //    String itemName1 = comboBox.GetItemText(item1);
    //    String itemName2 = comboBox.GetItemText(item2);

    //    CompareInfo compInfo = (Application.CurrentCulture).CompareInfo;
    //    return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort);
    //  }
    //}

    //[ListBindable(false)]
    //public class ObjectCollection : IList
    //{

    //  private ComboBoxEh owner;
    //  private ArrayList innerList;
    //  private IComparer comparer;

    //  public ObjectCollection(ComboBoxEh owner)
    //  {
    //    this.owner = owner;
    //  }

    //  private IComparer Comparer
    //  {
    //    get
    //    {
    //      if (comparer == null)
    //      {
    //        comparer = new ItemComparer(owner);
    //      }
    //      return comparer;
    //    }
    //  }

    //  private ArrayList InnerList
    //  {
    //    get
    //    {
    //      if (innerList == null)
    //      {
    //        innerList = new ArrayList();
    //      }
    //      return innerList;
    //    }
    //  }

    //  public int Count
    //  {
    //    get
    //    {
    //      return InnerList.Count;
    //    }
    //  }

    //  object ICollection.SyncRoot
    //  {
    //    get
    //    {
    //      return this;
    //    }
    //  }

    //  bool ICollection.IsSynchronized
    //  {
    //    get
    //    {
    //      return false;
    //    }
    //  }

    //  bool IList.IsFixedSize
    //  {
    //    get
    //    {
    //      return false;
    //    }
    //  }

    //  public bool IsReadOnly
    //  {
    //    get
    //    {
    //      return false;
    //    }
    //  }

    //  public int Add(object item)
    //  {
    //    owner.CheckNoDataSource();
    //    int index = AddInternal(item);
    //    //if (owner.UpdateNeeded() && owner.AutoCompleteSource == AutoCompleteSource.ListItems)
    //    //{
    //    //  owner.SetAutoComplete(false, false);
    //    //}
    //    return index;
    //  }


    //  private int AddInternal(object item)
    //  {

    //    if (item == null)
    //    {
    //      throw new ArgumentNullException("item");
    //    }
    //    int index = -1;
    //    if (!owner.sorted)
    //    {
    //      InnerList.Add(item);
    //    }
    //    else
    //    {
    //      index = InnerList.BinarySearch(item, Comparer);
    //      if (index < 0)
    //      {
    //        index = ~index; // getting the index of the first element that is larger than the search value
    //      }

    //      Debug.Assert(index >= 0 && index <= InnerList.Count, "Wrong index for insert");

    //      InnerList.Insert(index, item);
    //    }

    //    return index;
    //  }

    //  int IList.Add(object item)
    //  {
    //    return Add(item);
    //  }

    //  public void AddRange(object[] items)
    //  {
    //    owner.CheckNoDataSource();
    //    //owner.BeginUpdate();
    //    try
    //    {
    //      AddRangeInternal(items);
    //    }
    //    finally
    //    {
    //      //owner.EndUpdate();
    //      owner.ItemsChanged();
    //    }
    //  }

    //  internal void AddRangeInternal(IList items)
    //  {

    //    if (items == null)
    //      throw new ArgumentNullException("items");

    //    foreach (object item in items)
    //    {
    //      AddInternal(item);
    //    }

    //    //if (owner.AutoCompleteSource == AutoCompleteSource.ListItems)
    //    //{
    //    //  owner.SetAutoComplete(false, false);
    //    //}
    //  }

    //  [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    //  public virtual object this[int index]
    //  {
    //    get
    //    {
    //      if (index < 0 || index >= InnerList.Count)
    //      {
    //        throw new ArgumentOutOfRangeException("index", "InvalidArgument");
    //      }

    //      return InnerList[index];
    //    }

    //    set
    //    {
    //      owner.CheckNoDataSource();
    //      SetItemInternal(index, value);
    //    }
    //  }

    //  public void Clear()
    //  {
    //    owner.CheckNoDataSource();
    //    ClearInternal();
    //  }

    //  internal void ClearInternal()
    //  {
    //    InnerList.Clear();
    //    owner.selectedIndex = -1;
    //  }

    //  public bool Contains(object value)
    //  {
    //    return IndexOf(value) != -1;
    //  }

    //  public void CopyTo(object[] destination, int arrayIndex)
    //  {
    //    InnerList.CopyTo(destination, arrayIndex);
    //  }

    //  void ICollection.CopyTo(Array destination, int index)
    //  {
    //    InnerList.CopyTo(destination, index);
    //  }

    //  public IEnumerator GetEnumerator()
    //  {
    //    return InnerList.GetEnumerator();
    //  }

    //  public int IndexOf(object value)
    //  {
    //    if (value == null)
    //    {
    //      throw new ArgumentNullException("value");
    //    }

    //    return InnerList.IndexOf(value);
    //  }

    //  public void Insert(int index, object item)
    //  {
    //    owner.CheckNoDataSource();

    //    if (item == null)
    //    {
    //      throw new ArgumentNullException("item");
    //    }

    //    if (index < 0 || index > InnerList.Count)
    //    {
    //      throw new ArgumentOutOfRangeException("index", "InvalidArgument");
    //    }

    //    if (owner.sorted)
    //      Add(item);
    //    else
    //      InnerList.Insert(index, item);
    //  }

    //  public void RemoveAt(int index)
    //  {
    //    owner.CheckNoDataSource();

    //    if (index < 0 || index >= InnerList.Count)
    //      throw new ArgumentOutOfRangeException("index", "InvalidArgument");

    //    InnerList.RemoveAt(index);

    //    if (!owner.IsHandleCreated && index < owner.selectedIndex)
    //    {
    //      owner.selectedIndex--;
    //    }
    //  }

    //  public void Remove(object value)
    //  {

    //    int index = InnerList.IndexOf(value);

    //    if (index != -1)
    //    {
    //      RemoveAt(index);
    //    }
    //  }

    //  internal void SetItemInternal(int index, object value)
    //  {
    //    if (value == null)
    //      throw new ArgumentNullException("value");

    //    if (index < 0 || index >= InnerList.Count)
    //      throw new ArgumentOutOfRangeException("index", "InvalidArgument");

    //    InnerList[index] = value;

    //  }

    //}
    private class ComboBoxMessageFilter : IMessageFilter
    {
      private readonly ComboBoxEh comboBox;

      public ComboBoxMessageFilter(ComboBoxEh comboBox)
      {
        this.comboBox = comboBox;
      }

      bool IMessageFilter.PreFilterMessage(ref Message m)
      {
        if (m.Msg == NativeMethods.WM_LBUTTONDOWN ||
            m.Msg == NativeMethods.WM_RBUTTONDOWN ||
            m.Msg == NativeMethods.WM_MBUTTONDOWN ||
            m.Msg == NativeMethods.WM_NCLBUTTONDOWN ||
            m.Msg == NativeMethods.WM_NCRBUTTONDOWN ||
            m.Msg == NativeMethods.WM_NCMBUTTONDOWN
           )
        {
          comboBox.AppMouseDown(ref m);
        }
        return false;
      }
    }
    #endregion internal classes
  }

  public class ComboBoxTextEditControl : TextEditControl
  {
    private bool mouseActive;

    public ComboBoxTextEditControl(BaseTextBoxEh textBox) : base(textBox)
    {
    }

    protected override CreateParams CreateParams
    {
      get
      {
        CreateParams createParams = base.CreateParams;
        createParams.Style = createParams.Style | NativeMethods.ES_MULTILINE;
        return createParams;
      }
    }

    protected override void WndProc(ref Message m)
    {
      switch (m.Msg)
      {
        case NativeMethods.WM_LBUTTONDOWN:
          break;

        case NativeMethods.WM_MOUSEACTIVATE:
          break;

      }

      base.WndProc(ref m);

      switch (m.Msg)
      {
        case NativeMethods.WM_SETFOCUS:
          SelectAll();
          break;

        case NativeMethods.WM_ACTIVATE:
          SelectAll();
          break;

        case NativeMethods.WM_LBUTTONDOWN:
          if (mouseActive == true)
          {
            mouseActive = false;
            SelectAll();
          }
          break;

        case NativeMethods.WM_MOUSEACTIVATE:
          if (!Focused)
            mouseActive = true;
          break;

      }
    }

    protected override void OnMouseDown(MouseEventArgs e)
    {
      //int ss = SelectionStart;
      //int sl = SelectionLength;
      base.OnMouseDown(e);
      //ss = SelectionStart;
      //sl = SelectionLength;
    }

    protected override void OnGotFocus(EventArgs e)
    {
      base.OnGotFocus(e);
    }

    //protected override void OnActivated(EventArgs e)
    //{

    //}

  }

  /// <summary>
  ///       Row options for DroppedDownBox of ComboBoxEh
  /// </summary>
  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class ComboBoxDropDownBoxRowOptions : IGridLineHost, IGridRowHeightOptionsOwner
  {
    #region privates
    private readonly GridLine horzLine;
    private readonly GridRowHeightOptions heightOptions;
    private readonly ComboBoxDropDownBox dropDownBox;
    #endregion

    #region constructor
    public ComboBoxDropDownBoxRowOptions(ComboBoxDropDownBox dropDownBox)
    {
      this.dropDownBox = dropDownBox;
      horzLine = new GridLine(this);
      heightOptions = new GridRowHeightOptions(this);
    }
    #endregion

    #region design-time properties
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public GridRowHeightOptions HeightOptions
    {
      get
      {
        return heightOptions;
      }
    }

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public GridLine HorzLine
    {
      get
      {
        return horzLine;
      }
    }
    #endregion

    #region methods
    //IGridLineHost
    void IGridLineHost.GridLineChanged(GridLine gl)
    {
      dropDownBox.RowOptionsLineChanged();
    }

    bool IGridLineHost.GridLineDefaultVisible(GridLine gl)
    {
      return dropDownBox.RowOptionsHorzLineDefaultVisible();
    }

    Color IGridLineHost.GridLineDefaultColor(GridLine gl)
    {
      return dropDownBox.RowOptionsHorzLineDefaultColor();
    }

    DashStyle IGridLineHost.GridLineDefaultStyle(GridLine gl)
    {
      return DashStyle.Solid;
    }

    //IGridRowHeightOptionsOwner
    void IGridRowHeightOptionsOwner.HeightOptionsChanged(GridRowHeightOptions heightOptions)
    {
      dropDownBox.RowOptinsHeightOptionsChanged();
    }

    bool IGridRowHeightOptionsOwner.HeightOptionsDefaultAutoExpand(GridRowHeightOptions heightOptions)
    {
      return false;
    }

    int IGridRowHeightOptionsOwner.HeightOptionsDefaultContentHeight(GridRowHeightOptions heightOptions)
    {
      return 1;
    }

    int IGridRowHeightOptionsOwner.HeightOptionsDefaultMaxContentHeight(GridRowHeightOptions heightOptions)
    {
      return 0;
    }

    GridRowHeightUnit IGridRowHeightOptionsOwner.HeightOptionsDefaultUnit(GridRowHeightOptions heightOptions)
    {
      return GridRowHeightUnit.TextLine;
    }

    //Other
    #endregion
  }

  /// <summary>
  ///       Defines properties for DroppedDown list box of ComboBoxEh
  /// </summary>
  [TypeConverter(typeof(ExpandableObjectConverter))]
  public class ComboBoxDropDownBox
  {
    private ComboBoxEh comboBox;
    #region privates
    private readonly ComboBoxDropDownBoxRowOptions rowOptions;
    private Color backColor = Color.Empty;
    private bool backColorStored;
    private Color foreColor = Color.Empty;
    private bool foreColorStored;
    private Font font;
    #endregion

    #region constructor
    public ComboBoxDropDownBox(ComboBoxEh comboBox)
    {
      this.comboBox = comboBox;
      rowOptions = new ComboBoxDropDownBoxRowOptions(this);
      HeightInRows = 20;
    }
    #endregion

    #region design-time properties
    /// <summary>
    ///     Controls text alignment in the DroppedDown list box.
    /// </summary>
    [DefaultValue(DropDownAlign.Left)]
    public DropDownAlign Align { get; set; }

    /// <summary>
    ///     Indicates whether the list box is shown immediately on first key press in the editbox.
    /// </summary>
    [DefaultValue(false)]
    public bool AutoDrop { get; set; }

    /// <summary>
    ///       Specify the height of the DroppedDown list box unsing hight of one row 
    ///       in the list as a measure unit
    /// </summary>
    [DefaultValue(20)]
    public int HeightInRows { get; set; }

    /// <summary>
    ///       Indicates whether to show SizeGrip in the corner of the list
    ///       SizeGrip allows to resize list using mouse
    /// </summary>
    [DefaultValue(true)]
    public bool Sizable { get; set; }

    /// <summary>
    ///       Specify the width of the DroppedDown list box.
    ///       0 means that the width of the list box will be same as width of editBox.
    /// </summary>
    [DefaultValue(0)]
    public int Width { get; set; }

    /// <summary>
    ///       Define options of a row in the DroppedDow list box.
    /// </summary>
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public ComboBoxDropDownBoxRowOptions RowOptions
    {
      get { return rowOptions; }
    }

    /// <summary>
    ///     Indicates whether the list values of DroppedDownBox should by filtered
    ///     by the value of the typed text in the editor
    /// </summary>
    [DefaultValue(false)]
    public bool AutoFilter { get; set; }

    /// <summary>
    ///     The type of filtering of rows when AutoFilter = true.  
    ///     The value would come from EhLib.ComboBoxFilterType enumeration.
    /// </summary>
    [DefaultValue(ComboBoxFilterType.StartOfText)]
    public ComboBoxFilterType FilterType { get; set; }

    /// <summary>
    ///     The background color of this DroppedDown list box.
    /// </summary>
    public Color BackColor
    {
      get
      {
        if (backColorStored)
          return backColor;
        else
          return DefaultBackColor();
      }
      set
      {
        if ((backColorStored == false) || (backColor != value))
        {
          backColorStored = true;
          backColor = value;
          BackColorChanged();
        }
      }
    }

    ///     The foreground color of the DroppedDown list box.
    public Color ForeColor
    {
      get
      {
        if (foreColorStored)
          return foreColor;
        else
          return DefaultForeColor();
      }
      set
      {
        if ((foreColorStored == false) || (foreColor != value))
        {
          foreColorStored = true;
          foreColor = value;
          ForeColorChanged();
        }
      }
    }

    /// <summary>
    ///     Retrieves the current font for DroppedDown list box.
    /// </summary>
    public Font Font
    {
      get
      {
        if (font != null)
          return font;
        else
          return DefaultFont();
      }
      set
      {
        font = value;
        FontChanged();
      }
    }
    #endregion

    #region methods
    //BackColor
    protected virtual void BackColorChanged()
    {
    }

    public virtual Color DefaultBackColor()
    {
      if (comboBox != null)
        return comboBox.BackColor;
      else
        return Color.Empty;
    }

    protected virtual bool ShouldSerializeBackColor()
    {
      return (backColorStored == true);
    }

    public virtual void ResetBackColor()
    {
      backColorStored = false;
      BackColorChanged();
    }

    //ForeColor
    protected virtual void ForeColorChanged()
    {
    }

    public virtual Color DefaultForeColor()
    {
      if (comboBox != null)
        return comboBox.ForeColor;
      else
        return Color.Empty;
    }

    protected internal virtual bool ShouldSerializeForeColor()
    {
      return (foreColorStored == true);
    }

    public virtual void ResetForeColor()
    {
      foreColorStored = false;
      ForeColorChanged();
    }

    //Font
    protected virtual void FontChanged()
    {
    }

    public virtual Font DefaultFont()
    {
      if (comboBox != null)
        return comboBox.Font;
      else
        return null;
    }

    public virtual bool ShouldSerializeFont()
    {
      return (font != null);
    }

    public virtual void ResetFont()
    {
      if (font != null)
      {
        font = null;
        FontChanged();
      }
    }

    //Other
    internal void RowOptinsHeightOptionsChanged()
    {
    }

    internal Color RowOptionsHorzLineDefaultColor()
    {
      return SystemColors.ButtonFace;
    }

    internal bool RowOptionsHorzLineDefaultVisible()
    {
      return false;
    }

    internal void RowOptionsLineChanged()
    {
    }
    #endregion
  }

  //public class ListDataLink
  //{
  //  #region privates
  //  Control control;

  //  private object dataSource;
  //  private CurrencyManager dataManager;
  //  private BindingMemberInfo displayMember;
  //  private BindingMemberInfo valueMember;

  //  private bool inSetDataConnection = false;
  //  private bool isDataSourceInitEventHooked;
  //  private bool isDataSourceInitialized;
  //  #endregion privates

  //  public ListDataLink(Control control)
  //  {
  //    this.control = control;
  //  }

  //  #region properties
  //  public object DataSource
  //  {
  //    get
  //    {
  //      return dataSource;
  //    }

  //    set
  //    {
  //      if (value != null && !(value is IList || value is IListSource))
  //        throw new ArgumentException("BadDataSourceForComplexBinding");
  //      if (dataSource == value)
  //        return;
  //      try
  //      {
  //        SetDataConnection(value, displayMember, false);
  //      }
  //      catch
  //      {
  //        DisplayMember = "";
  //      }
  //      if (value == null)
  //        DisplayMember = "";
  //    }
  //  }

  //  protected CurrencyManager DataManager
  //  {
  //    get
  //    {
  //      return this.dataManager;
  //    }
  //  }

  //  public string DisplayMember
  //  {
  //    get
  //    {
  //      return displayMember.BindingMember;
  //    }

  //    set
  //    {
  //      BindingMemberInfo oldDisplayMember = displayMember;
  //      try
  //      {
  //        SetDataConnection(dataSource, new BindingMemberInfo(value), false);
  //      }
  //      catch
  //      {
  //        displayMember = oldDisplayMember;
  //      }
  //    }
  //  }

  //  public string ValueMember
  //  {
  //    get
  //    {
  //      return valueMember.BindingMember;
  //    }

  //    set
  //    {
  //      if (value == null)
  //        value = "";
  //      BindingMemberInfo newValueMember = new BindingMemberInfo(value);
  //      BindingMemberInfo oldValueMember = valueMember;
  //      if (!newValueMember.Equals(valueMember))
  //      {
  //        if (DisplayMember.Length == 0)
  //          SetDataConnection(DataSource, newValueMember, false);
  //        if (this.dataManager != null && value != null && value.Length != 0)
  //          if (!BindingMemberInfoInDataManager(newValueMember))
  //          {
  //            throw new ArgumentException("ListControlWrongValueMember");
  //          }

  //        valueMember = newValueMember;
  //        //OnValueMemberChanged(EventArgs.Empty);
  //        //OnSelectedValueChanged(EventArgs.Empty);
  //      }
  //    }
  //  }

  //  public int SelectedIndex
  //  {
  //    get;
  //    set;
  //  }

  //  public object SelectedValue
  //  {
  //    get
  //    {
  //      if (SelectedIndex != -1 && dataManager != null)
  //      {
  //        object currentItem = dataManager.List[SelectedIndex];
  //        object filteredItem = FilterItemOnProperty(currentItem, valueMember.BindingField);
  //        return filteredItem;
  //      }
  //      return null;
  //    }
  //    set
  //    {
  //      if (dataManager != null)
  //      {
  //        string propertyName = valueMember.BindingField;
  //        // we can't set the SelectedValue property when the listManager does not
  //        // have a ValueMember set.
  //        if (string.IsNullOrEmpty(propertyName))
  //          throw new InvalidOperationException("ListControlEmptyValueMemberInSettingSelectedValue");
  //        PropertyDescriptorCollection props = dataManager.GetItemProperties();
  //        PropertyDescriptor property = props.Find(propertyName, true);
  //        int index = FindInDataManager(property, value, true);
  //        this.SelectedIndex = index;
  //      }
  //    }
  //  }
  //  #endregion properties

  //  #region methods
  //  protected int FindInDataManager(PropertyDescriptor property, object key, bool keepIndex)
  //  {
  //    if (key == null)
  //    {
  //      throw new ArgumentNullException("key");
  //    }
  //    if (((property != null) && (dataManager.List is IBindingList)) && ((IBindingList)dataManager.List).SupportsSearching)
  //    {
  //      return ((IBindingList)dataManager.List).Find(property, key);
  //    }
  //    for (int i = 0; i < this.dataManager.List.Count; i++)
  //    {
  //      object obj2 = property.GetValue(dataManager.List[i]);
  //      if (key.Equals(obj2))
  //      {
  //        return i;
  //      }
  //    }
  //    return -1;
  //  }

  //  protected object FilterItemOnProperty(object item)
  //  {
  //    return FilterItemOnProperty(item, displayMember.BindingField);
  //  }

  //  protected object FilterItemOnProperty(object item, string field)
  //  {
  //    if (item != null && field.Length > 0)
  //    {
  //      try
  //      {
  //        // if we have a dataSource, then use that to display the string
  //        PropertyDescriptor descriptor;
  //        if (this.dataManager != null)
  //          descriptor = this.dataManager.GetItemProperties().Find(field, true);
  //        else
  //          descriptor = TypeDescriptor.GetProperties(item).Find(field, true);
  //        if (descriptor != null)
  //        {
  //          item = descriptor.GetValue(item);
  //        }
  //      }
  //      catch
  //      {
  //      }
  //    }
  //    return item;
  //  }

  //  private bool BindingMemberInfoInDataManager(BindingMemberInfo bindingMemberInfo)
  //  {
  //    if (dataManager == null)
  //      return false;
  //    PropertyDescriptorCollection props = dataManager.GetItemProperties();
  //    int propsCount = props.Count;

  //    for (int i = 0; i < propsCount; i++)
  //    {
  //      if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
  //        continue;
  //      if (props[i].Name.Equals(bindingMemberInfo.BindingField))
  //      {
  //        return true;
  //      }
  //    }

  //    for (int i = 0; i < propsCount; i++)
  //    {
  //      if (typeof(IList).IsAssignableFrom(props[i].PropertyType))
  //        continue;
  //      if (String.Compare(props[i].Name, bindingMemberInfo.BindingField, true, CultureInfo.CurrentCulture) == 0)
  //      {
  //        return true;
  //      }
  //    }

  //    return false;
  //  }

  //  private void DataManager_PositionChanged(object sender, EventArgs e)
  //  {
  //  }

  //  private void DataManager_ItemChanged(object sender, ItemChangedEventArgs e)
  //  {
  //  }

  //  private void DataSourceDisposed(object sender, EventArgs e)
  //  {
  //    Debug.Assert(sender == this.dataSource, "dispose notification for other than our dataSource?");
  //    SetDataConnection(null, new BindingMemberInfo(""), true);
  //  }

  //  private void DataSourceInitialized(object sender, EventArgs e)
  //  {
  //    ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);
  //    Debug.Assert(dsInit != null, "ListControl: ISupportInitializeNotification.Initialized event received, but current DataSource does not support ISupportInitializeNotification!");
  //    Debug.Assert(dsInit.IsInitialized, "ListControl: DataSource sent ISupportInitializeNotification.Initialized event but before it had finished initializing.");
  //    SetDataConnection(this.dataSource, this.displayMember, true);
  //  }

  //  private void SetDataConnection(object newDataSource, BindingMemberInfo newDisplayMember, bool force)
  //  {
  //    bool dataSourceChanged = dataSource != newDataSource;
  //    bool displayMemberChanged = !displayMember.Equals(newDisplayMember);

  //    if (inSetDataConnection)
  //    {
  //      return;
  //    }
  //    try
  //    {
  //      if (force || dataSourceChanged || displayMemberChanged)
  //      {
  //        inSetDataConnection = true;
  //        IList currentList = this.DataManager != null ? this.DataManager.List : null;
  //        bool currentManagerIsNull = this.DataManager == null;

  //        UnwireDataSource();

  //        dataSource = newDataSource;
  //        displayMember = newDisplayMember;

  //        WireDataSource();

  //        if (isDataSourceInitialized)
  //        {

  //          CurrencyManager newDataManager = null;
  //          if (newDataSource != null && control.BindingContext != null && !(newDataSource == Convert.DBNull))
  //          {
  //            newDataManager = (CurrencyManager)control.BindingContext[newDataSource, newDisplayMember.BindingPath];
  //          }

  //          if (dataManager != newDataManager)
  //          {
  //            if (dataManager != null)
  //            {
  //              dataManager.ItemChanged -= new ItemChangedEventHandler(DataManager_ItemChanged);
  //              dataManager.PositionChanged -= new EventHandler(DataManager_PositionChanged);
  //            }

  //            dataManager = newDataManager;

  //            if (dataManager != null)
  //            {
  //              dataManager.ItemChanged += new ItemChangedEventHandler(DataManager_ItemChanged);
  //              dataManager.PositionChanged += new EventHandler(DataManager_PositionChanged);
  //            }
  //          }

  //          if (dataManager != null && (displayMemberChanged || dataSourceChanged) && displayMember.BindingMember != null && displayMember.BindingMember.Length != 0)
  //          {

  //            if (!BindingMemberInfoInDataManager(displayMember))
  //              throw new ArgumentException("ListControlWrongDisplayMember");
  //          }

  //          if (dataManager != null && (dataSourceChanged || displayMemberChanged || force))
  //          {
  //            if (displayMemberChanged || (force && (currentList != this.dataManager.List || currentManagerIsNull)))
  //            {
  //              DataManager_ItemChanged(dataManager, null);
  //            }
  //          }
  //        }
  //        //this.displayMemberConverter = null;
  //      }

  //      //if (dataSourceChanged)
  //      //{
  //      //  OnDataSourceChanged(EventArgs.Empty);
  //      //}

  //      //if (displayMemberChanged)
  //      //{
  //      //  OnDisplayMemberChanged(EventArgs.Empty);
  //      //}
  //    }
  //    finally
  //    {
  //      inSetDataConnection = false;
  //    }
  //  }

  //  private void UnwireDataSource()
  //  {
  //    if (this.dataSource is IComponent)
  //    {
  //      ((IComponent)this.dataSource).Disposed -= new EventHandler(DataSourceDisposed);
  //    }

  //    ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);

  //    if (dsInit != null && isDataSourceInitEventHooked)
  //    {
  //      dsInit.Initialized -= new EventHandler(DataSourceInitialized);
  //      isDataSourceInitEventHooked = false;
  //    }
  //  }

  //  private void WireDataSource()
  //  {
  //    if (this.dataSource is IComponent)
  //    {
  //      ((IComponent)this.dataSource).Disposed += new EventHandler(DataSourceDisposed);
  //    }

  //    ISupportInitializeNotification dsInit = (this.dataSource as ISupportInitializeNotification);

  //    if (dsInit != null && !dsInit.IsInitialized)
  //    {
  //      dsInit.Initialized += new EventHandler(DataSourceInitialized);
  //      isDataSourceInitEventHooked = true;
  //      isDataSourceInitialized = false;
  //    }
  //    else
  //    {
  //      isDataSourceInitialized = true;
  //    }
  //  }
  //  #endregion methods

  //}

}
