using System; |
using System.Collections; |
using System.Collections.Generic; |
using System.Collections.ObjectModel; |
using System.ComponentModel; |
using System.Drawing; |
using System.Linq; |
using System.Windows.Forms; |
using Telerik.WinControls.UI; |
using Telerik.WinControls; |
|
namespace Our.Win.Core.Controls |
{ |
public partial class OurRadCombo : RadMultiColumnComboBox |
{ |
private bool _autoComplete = true; |
private bool _autoTab = true; |
private string _displayMember = ""; |
private DisplayColumnInfo[] _columns; |
private bool _columnsSet; |
|
private readonly List<ComboSearchData> _searchData = new List<ComboSearchData>(); |
|
public override string ThemeClassName |
{ |
|
get |
{ |
return "Telerik.WinControls.UI.RadMultiColumnComboBox"; |
} |
|
set |
{ |
|
base.ThemeClassName = value; |
|
} |
|
} |
|
public OurRadCombo() |
{ |
Init(); |
} |
|
public OurRadCombo(IContainer container) |
{ |
container.Add(this); |
Init(); |
} |
|
private void Init() |
{ |
InitializeComponent(); |
InitializeLayout(); |
|
MultiColumnComboBoxElement.TextBoxElement.KeyPress += OurRadComboKeyPress; |
MultiColumnComboBoxElement.TextBoxElement.KeyDown += OurRadComboKeyDown; |
DropDownClosed += OurRadComboDropDownClosed; |
//DropDownStyle = RadDropDownStyle.DropDownList; |
MultiColumnComboBoxElement.EditorControl.MasterGridViewTemplate.ShowRowHeaderColumn = false; |
AutoFilter = true; |
|
EditorControl.CellClick += EditorControlCellClick; |
ThemeName = "Vista"; |
|
//disable fade animation - takes forever on farm |
//Had to comment this out, sometimes dropdown doesn't close, if list is longer than visible height |
//MultiColumnPopupForm.FadeAnimationType = FadeAnimationType.None; |
|
//This has the same effect as setting the fade to none, but doesn't prevent the drop-down from closing. |
MultiColumnPopupForm.FadeAnimationFrames = 1; |
|
} |
|
void EditorControlCellClick(object sender, GridViewCellEventArgs e) |
{ |
SetNextControl(); |
} |
|
protected override void OnLoad(Size desiredSize) |
{ |
base.OnLoad(desiredSize); |
|
|
((GridTableElement)MultiColumnComboBoxElement.EditorControl.GridElement). |
AlternatingRowColor = Colors.OurAlternateRow; |
|
} |
|
public new string DisplayMember |
{ |
get { return _displayMember; } |
set |
{ |
base.DisplayMember = value; |
if (value != _displayMember) |
{ |
_displayMember = value; |
SetSearchData(); |
} |
} |
} |
|
void OurRadComboDropDownClosed(object sender, RadPopupClosedEventArgs args) |
{ |
// works but looks funky |
//SetNextControl(); |
} |
|
#region Properties |
|
/// <summary> |
/// Sorry, we only allow DropDownList style |
/// </summary> |
[Browsable(true), |
DefaultValue(true), |
Description("Sorry, we only allow DropDownList style. The setter property is only for backwards compatibility")] |
[Obsolete] |
public new RadDropDownStyle DropDownStyle |
{ |
get { return RadDropDownStyle.DropDownList; } |
set { /*only provided for backward compatibility*/ ; } |
} |
|
|
/// <summary> |
/// Use AutoComplete for this control |
/// </summary> |
[Browsable(true), |
DefaultValue(true), |
Description("Use AutoComplete for this control")] |
public bool AutoComplete |
{ |
get { return _autoComplete; } |
set { _autoComplete = value; } |
} |
|
|
|
/// <summary> |
/// Use AutoTab (to next control in tab stop) for this control |
/// </summary> |
[Browsable(true), |
DefaultValue(true), |
Description("Use AutoTab for this control causing the focus to move on to the next Tab Stop when the DropDown is closed")] |
public bool AutoTab |
{ |
get { return _autoTab; } |
set { _autoTab = value; } |
} |
|
|
/// <summary> |
/// Gets or sets the dropdown's DataSource. Sets selected index to -1. |
/// </summary> |
public new object DataSource |
{ |
get |
{ |
return base.DataSource; |
} |
|
set |
{ |
base.DataSource = value; |
SetDataSourceCommon(); |
} |
} |
|
private bool _rowColors = true; |
/// <summary> |
/// UseAlternatingRowColors |
/// </summary> |
[Browsable(true), |
DefaultValue(true), |
Description("Use alternating row colors in drop down grid")] |
public bool UseAlternatingRowColors |
{ |
get { return _rowColors; } |
set |
{ |
_rowColors = value; |
MultiColumnComboBoxElement.EditorControl.EnableAlternatingRowColor = _rowColors; |
} |
} |
|
private bool _columnHeaders = true; |
/// <summary> |
/// Show column headers. |
/// </summary> |
/// |
[Browsable(true), |
DefaultValue(true), |
Description("Shows or hides column header row."), |
EditorBrowsable(EditorBrowsableState.Always), |
Localizable(true)] |
public bool ShowColumnHeaders |
{ |
get |
{ |
return _columnHeaders; |
} |
|
set |
{ |
_columnHeaders = value; |
MultiColumnComboBoxElement.EditorControl.MasterGridViewTemplate.ShowColumnHeaders = _columnHeaders; |
} |
} |
|
private bool _rowSelectColumn; |
/// <summary> |
/// Show or hide row header column. |
/// </summary> |
[Browsable(true), |
DefaultValue(false), |
Description("Shows or hides column selection row."), |
EditorBrowsable(EditorBrowsableState.Always), |
Localizable(true)] |
public bool ShowRowSelectColumn |
{ |
get { return _rowSelectColumn; } |
set |
{ |
_rowSelectColumn = value; |
MultiColumnComboBoxElement.EditorControl.MasterGridViewTemplate.ShowRowHeaderColumn = _rowSelectColumn; |
} |
} |
|
|
/// <summary> |
/// Overide the Value property so that it will get and set an empty string when a null is get or set. |
/// This is used to work-around the inability to handle nulls in this version. |
/// </summary> |
public bool ReplaceNullValueWithEmptyString |
{ get; set; } |
|
#endregion |
|
#region Methods |
|
|
/// <summary> |
/// Sets basic properties for look and feel of control. |
/// </summary> |
private void InitializeLayout() |
{ |
MultiColumnComboBoxElement.EditorControl.MasterGridViewTemplate.AutoSizeColumnsMode = |
GridViewAutoSizeColumnsMode.Fill; |
} |
|
/// <summary> |
/// Autosizes columns in drop down. |
/// </summary> |
public void AutoSizeAllColumns() |
{ |
MultiColumnComboBoxElement.EditorControl.MasterGridViewTemplate.BestFitColumns(); |
} |
|
/// <summary> |
/// Hides columns that are not present in the provided DisplayColumnInfo array. |
/// </summary> |
/// <param name="columns"></param> |
public void ShowColumns(params DisplayColumnInfo[] columns) |
{ |
try |
{ |
// save in case datasource changes which sets all columns to isVisible |
_columns = columns; |
|
foreach (GridViewDataColumn column in MultiColumnComboBoxElement.Columns) |
{ |
column.IsVisible = false; |
|
foreach (DisplayColumnInfo colInfo in columns) |
{ |
if (colInfo.ColumnName == column.UniqueName) |
{ |
column.IsVisible = true; |
column.HeaderText = colInfo.ColumnCaption; |
column.SortOrder = colInfo.Order; |
} |
} |
} |
_columnsSet = true; |
} |
catch (Exception) |
{ |
{ } |
} |
} |
|
/// <summary> |
/// Hides columns that are not present in the provided string array. |
/// </summary> |
/// <param name="cols"></param> |
public void ShowColumns(string[] cols) |
{ |
try |
{ |
_columns = new DisplayColumnInfo[cols.Count()]; |
var colidx = 0; |
|
foreach (GridViewDataColumn column in MultiColumnComboBoxElement.Columns) |
{ |
column.IsVisible = false; |
|
foreach (string str in cols) |
{ |
if (str == column.UniqueName) |
{ |
column.IsVisible = true; |
|
if (string.IsNullOrEmpty(column.HeaderText)) |
{ |
column.HeaderText = str; |
} |
} |
_columns[colidx] = new DisplayColumnInfo(str, column.HeaderText); |
} |
} |
_columnsSet = true; |
} |
catch (Exception) |
{ |
{ } |
} |
} |
|
/// <summary> |
/// Provides a mechanism to bind an array of strings to the multi-column combo box. RadGrid will display |
/// only a string's length unless you bind to a list of objects that exposes a string property. |
/// </summary> |
public void DataSourceAsBindingList(string[] str) |
{ |
var bindList = new BindingList<ObjectList>(); |
foreach (string s in str) |
{ |
bindList.Add(new ObjectList(s)); |
} |
DataSource = bindList; |
} |
|
/// <summary> |
/// Bind a Ilist of type string to a BindingList of type that the combo grid can use. |
/// </summary> |
/// <param name="col"></param> |
public void DataSourceAsBindingList(IList<string> col) |
{ |
var bindList = new BindingList<ObjectList>(); |
foreach (string s in col) |
{ |
bindList.Add(new ObjectList(s)); |
} |
DataSource = bindList; |
} |
|
/// <summary> |
/// Bind a ReadOnlyCollection of type string to a BindingList of type that the combo grid can use. |
/// </summary> |
/// <param name="col"></param> |
public void DataSourceAsBindingList(ReadOnlyCollection<string> col) |
{ |
var bindList = new BindingList<ObjectList>(); |
foreach (string s in col) |
{ |
bindList.Add(new ObjectList(s)); |
} |
DataSource = bindList; |
} |
|
/// <summary> |
/// Binds a list of type T to the grid using a BindingList. Use this method when the Telerik |
/// grid does not bind to something that a control should be able to bind to. |
/// </summary> |
/// <typeparam name="T"></typeparam> |
/// <param name="list"></param> |
public void DataSourceAsBindingList<T>(List<T> list) |
{ |
var bindList = new BindingList<ObjectList>(); |
|
foreach (var item in list) |
{ |
bindList.Add(new ObjectList(item)); |
} |
DataSource = bindList; |
} |
|
/// <summary> |
/// Binds an ArrayList to the grid using a BindingList. Use this method when the Telerik |
/// grid does not bind to something that a control should be able to bind to. |
/// </summary> |
/// <param name="list"></param> |
public void DataSourceAsBindingList(ArrayList list) |
{ |
var bindList = new BindingList<ObjectList>(); |
|
foreach (var item in list) |
{ |
bindList.Add(new ObjectList(item)); |
} |
DataSource = bindList; |
} |
|
/// <summary> |
/// Handles the common processes of setting the datasource |
/// </summary> |
private void SetDataSourceCommon() |
{ |
SetSearchData(); |
if (_columnsSet) |
ShowColumns(_columns); |
|
SelectedIndex = -1; |
} |
|
public int FindExact(string find) |
{ |
return FindStringExact(find, _searchData); |
} |
|
|
public object FindItemExact(object find) |
{ |
var idx = FindStringExact(find.ToString(), _searchData); |
if (idx == -1) |
return null; |
return from o in _searchData where o.Idx == idx select o.Val; |
} |
|
public object FindItemExactAndSetIndex(object find) |
{ |
int idx = FindStringExact(find.ToString(), _searchData); |
if (idx == -1) |
return null; |
SelectedIndex = idx; |
return from o in _searchData where o.Idx == idx select o.Val; |
} |
|
private static int FindString(string find, IEnumerable<ComboSearchData> searchData) |
{ |
var found = (from o in searchData |
where o.Val.Trim().ToUpper().StartsWith(find.Trim().ToUpper()) |
select o).FirstOrDefault(); |
|
if (found != null) |
return found.Idx; |
return -1; |
} |
|
private static int FindString(string find, IEnumerable<ComboSearchData> searchData, int selectedIndex) |
{ |
var found = (from o in searchData |
where o.Val.Trim().ToUpper().StartsWith(find.Trim().ToUpper()) && o.Idx > selectedIndex |
select o).FirstOrDefault(); |
|
if (found != null) |
return found.Idx; |
return -1; |
} |
|
private static int FindStringExact(string find, IEnumerable<ComboSearchData> searchData) |
{ |
var found = (from o in searchData |
where o.Val.Trim().ToUpper() == find.Trim().ToUpper() |
select o).FirstOrDefault(); |
|
if (found != null) |
return found.Idx; |
return -1; |
} |
|
|
private void SetSearchData() |
{ |
var idx = 0; |
_searchData.Clear(); |
// |
// If the DisplayMember is not set then use cell index 0. |
// So, it would be a good idea to always set DisplayMember. |
// |
var cellidx = 0; |
if (_displayMember.Trim().Length > 0) |
if (EditorControl.MasterGridViewTemplate.Columns[_displayMember] == null) |
return; |
else |
cellidx = EditorControl.MasterGridViewTemplate.Columns[_displayMember].Index; |
|
|
foreach (var row in EditorControl.Rows) |
{ |
if (row.Cells[cellidx] == null) //check added by JustinG Mar 24, 2010 |
continue; |
if (row.Cells.Count == 1 && row.Cells[cellidx].ColumnInfo.UniqueName == "Length") |
// The Binding Source is an array. |
_searchData.Add(new ComboSearchData { Idx = idx++, Val = row.DataBoundItem.ToString() }); |
else |
_searchData.Add(new ComboSearchData { Idx = idx++, Val = row.Cells[cellidx].Value.ToString() }); |
} |
} |
|
private void OurRadComboKeyDown(object sender, KeyEventArgs e) |
{ |
if (e.KeyCode == Keys.Enter) |
{ |
SetNextControl(); |
e.Handled = true; |
return; |
} |
|
|
// Use the Delete or Escape Key to blank out the ComboBox and |
// allow the user to type in a new value |
if ((e.KeyCode == Keys.Delete) || |
(e.KeyCode == Keys.Escape)) |
{ |
SelectedIndex = -1; |
Text = ""; |
} |
} |
|
|
private void OurRadComboKeyPress(object sender, KeyPressEventArgs e) |
{ |
var idx = -1; |
string toFind; |
|
try |
{ |
if (!Char.IsControl(e.KeyChar)) |
{ |
if (AutoComplete) |
{ |
toFind = Text.Substring(0, ((RadTextBoxItem)FocusedElement).SelectionStart) + e.KeyChar; |
idx = FindStringExact(toFind, _searchData); |
|
if (idx == -1) |
{ |
// An exact match for the whole string was not found |
// Find a substring instead. |
idx = FindString(toFind, _searchData); |
} |
else |
{ |
// An exact match was found. Close the dropdown. |
EditorControl.CloseEditor();// DroppedDown = false; |
} |
|
if (idx != -1) // The substring was found. |
{ |
SelectedIndex = idx; |
((RadTextBoxItem)FocusedElement).SelectionStart = toFind.Length; |
((RadTextBoxItem)FocusedElement).SelectionLength = Text.Length - ((RadTextBoxItem)FocusedElement).SelectionStart; |
} |
else // The last keystroke did not create a valid substring. |
{ |
// If the substring is not found, cancel the keypress |
e.KeyChar = (char)0; |
} |
} |
else // AutoComplete = false. Treat it like a DropDownList by finding the |
// KeyChar that was struck starting from the current index |
{ |
idx = FindString(e.KeyChar.ToString(), _searchData, SelectedIndex); |
|
if (idx != -1) |
{ |
SelectedIndex = idx; |
} |
} |
} |
} |
// If you find the AutoComplete not working, check here. |
// You can always throw the exception but you will have to handle it. |
catch (Exception) |
{ |
e.KeyChar = (char)0; |
} |
|
// Do no allow the user to backspace over characters. Treat it like |
// a left arrow instead. The user must not be allowed to change the |
// value in the ComboBox. |
if ((e.KeyChar == (char)(Keys.Back)) && // A Backspace Key is hit |
//(_autoComplete) && // AutoComplete = true |
(Convert.ToBoolean(((RadTextBoxItem)FocusedElement).SelectionStart))) // And the SelectionStart is positive |
{ |
// Find a substring that is one character less the the current selection. |
// This mimicks moving back one space with an arrow key. This substring should |
// always exist since we don't allow invalid selections to be typed. If you're |
// on the 3rd character of a valid code, then the first two characters have to |
// be valid. Moving back to them and finding the 1st occurrence should never fail. |
toFind = Text.Substring(0, ((RadTextBoxItem)FocusedElement).SelectionStart - 1); |
idx = FindString(toFind, _searchData); |
|
if (idx != -1) |
{ |
SelectedIndex = idx; |
((RadTextBoxItem)FocusedElement).SelectionStart = toFind.Length; |
((RadTextBoxItem)FocusedElement).SelectionLength = Text.Length - ((RadTextBoxItem)FocusedElement).SelectionStart; |
} |
} |
|
// e.Handled is always true. We handle every keystroke programatically. |
e.Handled = true; |
} |
|
private void SetNextControl() |
{ |
// this actually works better than the Parent.SelectNextControl(this, true, false, true, true); |
if (_autoTab && SelectedIndex >= 0) |
//Parent.SelectNextControl(this, true, false, true, true); |
SendKeys.Send("{tab}"); |
|
// TODO: This needs some testing. Maybe only invoke if not _autoTab? |
InvokeInputConfirmed(); |
} |
|
#endregion |
|
|
#region InputConfirmed Event handling |
|
private event InputConfirmedEventHandler _inputConfirmed; |
|
[Browsable(true)] |
public event InputConfirmedEventHandler InputConfirmed |
{ |
add { _inputConfirmed += value; } |
remove { _inputConfirmed -= value; } |
} |
|
protected void InvokeInputConfirmed() |
{ |
if (_inputConfirmed != null) |
{ |
_inputConfirmed(this, new EventArgs()); |
} |
} |
|
public virtual void OnInputConfirmed(EventArgs e) |
{ |
if (_inputConfirmed != null) |
{ |
_inputConfirmed(this, e); |
} |
} |
|
#endregion |
|
|
|
#region Internal Helper Classes |
|
internal class ComboSearchData |
{ |
internal int Idx { get; set; } |
internal string Val { get; set; } |
} |
|
#endregion |
|
} |
|
#region Public Helper Classes |
public class DisplayColumnInfo |
{ |
|
public DisplayColumnInfo(string columnName, string columnCaption) |
{ |
ColumnName = columnName; |
ColumnCaption = columnCaption; |
} |
|
public DisplayColumnInfo(string columnName, string columnCaption, RadSortOrder order) |
{ |
ColumnName = columnName; |
ColumnCaption = columnCaption; |
Order = order; |
} |
|
public string ColumnName { get; set; } |
|
public string ColumnCaption { get; set; } |
|
public RadSortOrder Order { get; set; } |
|
} |
#endregion |
|
|
} |
|
|
|