using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Core.Common.Controls.Swf.Properties;
using Core.Common.Controls.Swf.Table.Editors;
using Core.Common.Controls.Swf.Table.Validation;
using Core.Common.Utils;
using Core.Common.Utils.Collections;
using Core.Common.Utils.Collections.Generic;
using Core.Common.Utils.Reflection;
using DevExpress.Data;
using DevExpress.Utils;
using DevExpress.Utils.Drawing;
using DevExpress.Utils.Menu;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Repository;
using DevExpress.XtraEditors.ViewInfo;
using DevExpress.XtraGrid;
using DevExpress.XtraGrid.Columns;
using DevExpress.XtraGrid.Localization;
using DevExpress.XtraGrid.Views.Base;
using DevExpress.XtraGrid.Views.Grid;
using DevExpress.XtraGrid.Views.Grid.ViewInfo;
using log4net;
using TypeConverter = Core.Common.Utils.TypeConverter;
namespace Core.Common.Controls.Swf.Table
{
///
/// Graphical representation of tabular data.
///
public partial class TableView : UserControl, IView, ISupportInitialize
{
public enum ValidationExceptionMode
{
Ignore,
NoAction,
DisplayError,
ThrowException
}
private static readonly ILog Log = LogManager.GetLogger(typeof(TableView));
private readonly EventedList selectedCells;
private readonly TableViewValidator tableViewValidator;
private readonly EventedList columns;
private bool isPasting;
private bool isSelectionChanging;
private bool updatingSelection;
private bool showRowNumbers;
private bool autoGenerateColumns;
private bool refreshRequired;
private bool allowColumnSorting = true;
private bool f2Pressed;
private Timer refreshTimer;
private BindingSource bindingSource;
private GridCell[] cellsToFill;
public TableView()
{
InitializeComponent();
columns = new EventedList();
ColumnMenuItems = new List();
selectedCells = new EventedList();
tableViewValidator = new TableViewValidator(this);
PasteController = new TableViewPasteController(this)
{
PasteBehaviour = TableViewPasteBehaviourOptions.SkipCellWhenValueIsInvalid
};
AllowColumnPinning = true;
AllowDeleteRow = true;
AutoGenerateColumns = true;
MultipleCellEdit = true;
dxGridView.OptionsView.ShowFooter = false;
dxGridView.OptionsBehavior.CopyToClipboardWithColumnHeaders = false; //mimic behavior of 8.2
GridLocalizer.Active = new TableViewExceptionMessageController();
Text = Resources.TableView_TableView_new_Table;
ReadOnlyCellForeColor = Color.Black; //just use black as a default to increase readability
ReadOnlyCellBackColor = Color.FromArgb(255, 244, 244, 244);
InvalidCellBackgroundColor = Color.Tomato;
ExceptionMode = ValidationExceptionMode.DisplayError;
ConfigureContextMenu();
SubscribeEvents();
CreateRefreshTimer();
}
public void ExportAsCsv(string fileName, string delimiter = ", ")
{
// the build-in export to file method depends on a devexpress dll we haven't included so far
using (var writer = new StreamWriter(fileName))
{
var visibleColumns = Columns.Where(c => c.Visible).OrderBy(c => c.DisplayIndex);
var lastColumn = visibleColumns.Last();
foreach (var visibleColumn in visibleColumns)
{
writer.Write(visibleColumn.Caption);
if (visibleColumn != lastColumn)
{
writer.Write(delimiter);
}
}
writer.WriteLine();
var shownColumns = visibleColumns.Select(c => c.AbsoluteIndex).ToArray();
//writing the data
for (int x = 0; x < RowCount; x++)
{
for (int y = 0; y < shownColumns.Length; y++)
{
writer.Write(GetCellDisplayText(x, shownColumns[y]));
if (y != Columns.Count - 1)
{
writer.Write(delimiter);
}
}
writer.WriteLine();
}
writer.Close();
}
}
///
/// Clean up any resources being used.
///
/// true if managed resources should be disposed; otherwise, false.
protected override void Dispose(bool disposing)
{
UnSubscribeFromDataSource();
if (refreshTimer != null)
{
refreshTimer.Stop();
refreshTimer.Tick -= OnRefreshTimerOnTick;
refreshTimer.Dispose();
refreshTimer = null;
}
if (disposing)
{
//only dispose if disposing managed resource (otherwise called from other thread)
// prevents memory leaks
foreach (var view in dxGridControl.Views.Cast().ToList())
{
view.Dispose();
}
dxGridControl.Dispose();
}
if (Columns != null)
{
foreach (var column in Columns)
{
column.Dispose();
}
}
if (disposing && (components != null))
{
components.Dispose();
}
try
{
/*
Localize bug in XtraEditors:
System.InvalidOperationException : Value Dispose() cannot be called while doing CreateHandle().
at System.Windows.Forms.Control.Dispose(Boolean disposing)
at DevExpress.XtraEditors.ScrollBarBase.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
at DevExpress.XtraGrid.Scrolling.ScrollInfo.Dispose()
at DevExpress.XtraGrid.Views.Grid.GridView.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
at DevExpress.XtraGrid.GridControl.RemoveView(BaseView gv, Boolean disposeView)
at DevExpress.XtraGrid.GridControl.RemoveView(BaseView gv)
at DevExpress.XtraGrid.GridControl.Dispose(Boolean disposing)
at System.ComponentModel.Component.Dispose()
at System.Windows.Forms.Control.Dispose(Boolean disposing)
at System.Windows.Forms.ContainerControl.Dispose(Boolean disposing)
at Core.Common.Controls.Swf.Table.TableView.Dispose(Boolean disposing) in TableView.Designer.cs: line 22
*/
base.Dispose(disposing);
}
catch (InvalidOperationException e)
{
Log.Debug("Strange bug in XtraGrid control, from time to time crashes", e);
}
}
///
/// Converts value pasted from clipboard, currently always a string, to a value of the type
/// required by the underlying datasource.
/// In addition to default conversion for e.g string to int by
/// TypeConverter.ConvertValueToTargetType
/// There is added support for the RepositoryItemImageComboBox where the description (will default be
/// copied to clipboard) will be converted to value.
/// - NB the base class RepositoryItemComboBox does not have items with value and description
///
///
///
///
internal object ConvertStringValueToDataValue(int columnIndex, string cellValue)
{
object value = null;
RepositoryItem editor = dxGridView.Columns[columnIndex].ColumnEdit;
Type columnType = dxGridView.Columns[columnIndex].ColumnType;
if (null != editor)
{
var imageComboBoxEditor = editor as RepositoryItemImageComboBox;
if (imageComboBoxEditor != null)
{
var repositoryItemImageComboBox = imageComboBoxEditor;
var map = new Dictionary();
for (int j = 0; j < repositoryItemImageComboBox.Items.Count; j++)
{
map[repositoryItemImageComboBox.Items[j].Description] =
repositoryItemImageComboBox.Items[j].Value;
}
if (map.ContainsKey(cellValue))
{
value = map[cellValue];
}
}
else if (editor is RepositoryItemComboBox || editor is RepositoryItemLookUpEdit)
{
var valueLookUp = new Dictionary();
var comboBoxEditor = editor as RepositoryItemComboBox;
if (comboBoxEditor != null)
{
var repositoryItemComboBox = comboBoxEditor;
valueLookUp = repositoryItemComboBox.Items.OfType().ToDictionary(i => i.DisplayText, i => i.Value);
}
else
{
var lookUpEdit = (RepositoryItemLookUpEdit) editor;
var comboBoxItems = lookUpEdit.DataSource as IEnumerable;
if (comboBoxItems != null)
{
valueLookUp = comboBoxItems.ToDictionary(i => i.DisplayText, i => i.Value);
}
}
if (valueLookUp.Any() && valueLookUp.ContainsKey(cellValue))
{
value = valueLookUp[cellValue];
}
else if (comboBoxEditor != null) // items are not mandatory
{
value = cellValue;
}
}
else
{
value = ConvertToColumnValue(cellValue, columnType);
}
}
else if (dxGridView.Columns[columnIndex].RealColumnEdit is RepositoryItemCheckEdit)
{
return cellValue == "Checked";
}
else
{
value = ConvertToColumnValue(cellValue, columnType);
}
return value;
}
private TableViewColumn GetColumnByDxColumn(GridColumn dxGridColumn)
{
return Columns.FirstOrDefault(c => c.AbsoluteIndex == dxGridColumn.AbsoluteIndex);
}
private void BestFitColumnsWithOnlyFirstWordOfHeader()
{
var oldHeaders = Columns.ToDictionary(c => c, c => c.Caption);
dxGridView.BeginUpdate();
try
{
foreach (var column in Columns)
{
string caption = column.Caption;
//Say the caption is 'Discharge on Lateral (m3/s)'. We're trying to do
//something a bit smarter than just ignore the header entirely: we try
//to get the first word (eg: "Discharge"), add room for the ellipsis
//and keep that into view
if (!String.IsNullOrEmpty(caption))
{
var indexOfWhitespace = caption.IndexOfAny(new[]
{
' ',
'\t'
});
if (indexOfWhitespace >= 0)
{
//add space for ellipsis
column.Caption = caption.Substring(0, indexOfWhitespace) + "...";
}
}
}
dxGridView.BestFitColumns();
//restore the original columns
foreach (var column in Columns)
{
column.Caption = oldHeaders[column];
}
}
finally
{
dxGridView.EndUpdate();
}
}
private bool DeselectSelectCells(int top, int left, int bottom, int right)
{
var cellsDeselected = false;
// dxGridControl.SuspendLayout();
dxGridView.BeginSelection();
for (int y = top; y <= bottom; y++)
{
for (int x = left; x <= right; x++)
{
var selectedCell = selectedCells.FirstOrDefault(c => c.Column == GetColumnByDisplayIndex(x) && c.RowIndex == y);
if (selectedCell != null)
{
selectedCells.Remove(selectedCell);
cellsDeselected = true;
}
}
}
dxGridView.EndSelection();
//dxGridControl.ResumeLayout();
return cellsDeselected;
}
private void AddEnumCellEditors()
{
foreach (var column in Columns.Where(c => c.ColumnType.IsEnum && c.Editor == null))
{
column.Editor = new ComboBoxTypeEditor
{
Items = Enum.GetValues(column.ColumnType),
ItemsMandatory = false,
CustomFormatter = new EnumFormatter(column.ColumnType)
};
}
}
private void ColumnsOnCollectionChanged(object sender, NotifyCollectionChangingEventArgs e)
{
if (e.Action == NotifyCollectionChangeAction.Remove && dxGridView.Columns.Count > e.Index)
{
dxGridView.Columns.RemoveAt(e.Index);
}
}
private static void CopyPasteControllerPasteFailed(object sender, EventArgs e)
{
MessageBox.Show(e.Value);
}
private void SelectedCellsCollectionChanged(object sender, NotifyCollectionChangingEventArgs e)
{
if (updatingSelection)
{
return;
}
var cell = (TableViewCell) e.Item;
var gridColumn = GetDxColumnByDisplayIndex(cell.Column.DisplayIndex);
switch (e.Action)
{
case NotifyCollectionChangeAction.Add:
dxGridView.SelectCell(cell.RowIndex, gridColumn);
break;
case NotifyCollectionChangeAction.Remove:
dxGridView.UnselectCell(cell.RowIndex, gridColumn);
break;
default:
throw new NotSupportedException(string.Format(Resources.TableView_SelectedCellsCollectionChanged_Action_0_is_not_supported_by_the_TableView, e.Action));
}
}
private void BindingListListChanged(object sender, ListChangedEventArgs e)
{
if (!AutoGenerateColumns || e.ListChangedType != ListChangedType.PropertyDescriptorChanged || e.PropertyDescriptor == null)
{
return;
}
// has column name changed
var column = dxGridView.Columns.ColumnByName(e.PropertyDescriptor.DisplayName);
if (column != null)
{
return;
}
column = dxGridView.Columns[e.NewIndex];
column.FieldName = e.PropertyDescriptor.Name;
column.Caption = e.PropertyDescriptor.DisplayName;
}
private void DataSourceDataTableColumnsCollectionChanged(object sender, CollectionChangeEventArgs e)
{
if (e.Action == CollectionChangeAction.Add || e.Action == CollectionChangeAction.Remove)
{
if (AutoGenerateColumns)
{
dxGridView.PopulateColumns();
BuildColumnWrappers();
BestFitColumns();
}
}
}
private void DataSourcePropertyChanged(object sender, PropertyChangedEventArgs e)
{
refreshRequired = true;
}
private void DataSourceCollectionChanged(object sender, NotifyCollectionChangingEventArgs e)
{
refreshRequired = true;
UpdateHeaderColumnSize();
}
///
/// Checks if the mouse is clicked at the Header Panel Button in the grid.
/// If yes select entire grid (TOOLS-2834)
/// cfr: http://documentation.devexpress.com/#WindowsForms/CustomDocument532
/// "The header panel button belongs to both the column header panel and row
/// indicator panel elements. By default, the header panel button has no special
/// functionality. When the View is used to represent detail data, the header
/// panel button serves as a zoom button."
///
///
///
private void HandleHeaderPanelButton(object sender, MouseEventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
var hitInfo = view.CalcHitInfo(new Point(e.X, e.Y));
if ((hitInfo.HitTest == GridHitTest.ColumnButton) && (hitInfo.InColumn == false))
{
view.SelectAll();
}
}
private static DXMenuItem GetItemByStringId(DXPopupMenu menu, GridStringId id)
{
return menu.Items.Cast().FirstOrDefault(item => ((GridStringId) item.Tag) == id);
}
private void SubscribeToDataSource()
{
if (dxGridControl.DataSource == null)
{
return;
}
var propertyChanged = dxGridControl.DataSource as INotifyPropertyChanged;
if (propertyChanged != null)
{
propertyChanged.PropertyChanged += DataSourcePropertyChanged;
}
var collectionChanged = dxGridControl.DataSource as INotifyCollectionChanged;
if (collectionChanged != null)
{
collectionChanged.CollectionChanged += DataSourceCollectionChanged;
}
var dataTable = dxGridControl.DataSource as DataTable;
if (dataTable != null)
{
var table = dataTable;
table.Columns.CollectionChanged += DataSourceDataTableColumnsCollectionChanged;
}
var bindingList = dxGridControl.DataSource as IBindingList;
if (bindingList != null)
{
bindingList.ListChanged += BindingListListChanged;
}
}
private void UnSubscribeFromDataSource()
{
if (dxGridControl == null || dxGridControl.DataSource == null)
{
return;
}
var propertyChanged = dxGridControl.DataSource as INotifyPropertyChanged;
if (propertyChanged != null)
{
propertyChanged.PropertyChanged -= DataSourcePropertyChanged;
}
var notifyCollectionChanged = dxGridControl.DataSource as INotifyCollectionChanged;
if (notifyCollectionChanged != null)
{
notifyCollectionChanged.CollectionChanged -= DataSourceCollectionChanged;
}
var dataTable = dxGridControl.DataSource as DataTable;
if (dataTable != null)
{
var table = dataTable;
table.Columns.CollectionChanged -= DataSourceDataTableColumnsCollectionChanged;
}
var bindingSource = dxGridControl.DataSource as BindingSource;
if (bindingSource != null)
{
bindingSource.ListChanged -= BindingListListChanged;
}
}
///
/// Calculate width of the header column based on grid font
///
private void UpdateHeaderColumnSize()
{
var indicatorWidth = (int) dxGridControl.Font.SizeInPoints*RowCount.ToString(CultureInfo.InvariantCulture).Length + 15;
dxGridView.IndicatorWidth = showRowNumbers ? indicatorWidth : -1;
Refresh();
}
///
/// Returns true if the whole selection is readonly. This can be on cell,column or table level If a cell is not it returns false.
///
///
private bool GetSelectionIsReadonly()
{
//no selection use tableview readonly
if (SelectedCells.Count == 0)
{
return ReadOnly;
}
//A selection is readonly if ALL cells are readonly (otherwise i can change the not readonly part)
return SelectedCells.All(cell => CellIsReadOnly(cell.RowIndex, cell.Column));
}
private void UpdateSelectionFromGridControl()
{
if (updatingSelection)
{
return;
}
updatingSelection = true;
selectedCells.Clear();
var gridViewSelectedCells = dxGridView.GetSelectedCells();
foreach (var cell in gridViewSelectedCells)
{
selectedCells.Add(new TableViewCell(cell.RowHandle, GetColumnByDxColumn(cell.Column)));
}
if (!IsEditing && !isPasting)
{
if (SelectionChanged != null)
{
Log.DebugFormat(Resources.TableView_UpdateSelectionFromGridControl_Firing_selection_changed_event);
SelectionChanged(this, new TableSelectionChangedEventArgs(selectedCells.ToArray()));
}
}
// Update the row context menu
RowContextMenu.Items.OfType().ForEach(mi => mi.Available = true);
RowContextMenu.Items.OfType()
.Where(mi => mi.Name == "btnDelete" || mi.Name == "btnPaste")
.ForEach(mi => mi.Available = !GetSelectionIsReadonly());
updatingSelection = false;
}
private void BuildColumnWrappers()
{
if (!AutoGenerateColumns)
{
return;
}
columns.CollectionChanged -= ColumnsOnCollectionChanged;
columns.Clear();
foreach (GridColumn dxColumn in dxGridView.Columns)
{
Columns.Add(new TableViewColumn(dxGridView, dxGridControl, dxColumn, this, false)
{
SortingAllowed = AllowColumnSorting
});
}
columns.CollectionChanged += ColumnsOnCollectionChanged;
}
private void ShowEditorIfRowSelect()
{
if (dxGridView.FocusedColumn != null && (RowSelect) && (dxGridView.ActiveEditor == null))
{
dxGridView.ShowEditor();
}
}
private GridColumn GetDxColumnByDisplayIndex(int displayIndex)
{
return dxGridView.Columns[Columns.First(c => c.DisplayIndex == displayIndex).AbsoluteIndex];
}
private void UpdateColumnHeaderMenu(GridMenuEventArgs e, TableViewColumn viewColumn)
{
//show grid menu is handled to remove menu-items. For grouping etc.
//No way to do this in a setting :(
//see http://community.devexpress.com/forums/t/61316.aspx
var ids = new[]
{
GridStringId.MenuColumnGroupBox,
GridStringId.MenuColumnGroup,
GridStringId.MenuColumnRemoveColumn
};
var dxMenuItems = ids.Select(id => GetItemByStringId(e.Menu, id));
var dxMenuItemsToHide = dxMenuItems.Where(item => item != null);
foreach (var item in dxMenuItemsToHide)
{
item.Visible = false;
}
if (AllowColumnPinning && viewColumn != null)
{
var pinColumnMenuItem = new DXMenuCheckItem
{
Caption = viewColumn.Pinned ? Resources.TableView_UpdateColumnHeaderMenu_Unpin_Column : Resources.TableView_UpdateColumnHeaderMenu_Pin_Column,
Checked = viewColumn.Pinned,
Image = Resources.pin
};
pinColumnMenuItem.CheckedChanged += (sender, args) => { viewColumn.Pinned = pinColumnMenuItem.Checked; };
e.Menu.Items.Add(pinColumnMenuItem);
}
var copyHeadersColumnMenuItem = new DXMenuItem
{
Caption = Resources.TableView_UpdateColumnHeaderMenu_Copy_all_headers,
Image = Resources.CopyHS
};
copyHeadersColumnMenuItem.Click += (sender, args) =>
{
var sb = new StringBuilder();
CopyHeader(sb);
Clipboard.Clear();
Clipboard.SetData(DataFormats.Text, sb.ToString());
};
e.Menu.Items.Add(copyHeadersColumnMenuItem);
}
private bool ValidateAndCommitRow(int rowIndex)
{
string errorText;
if (!tableViewValidator.ValidateRow(rowIndex, out errorText))
{
Log.ErrorFormat(Resources.TableView_ValidateAndCommitRow_Can_not_set_value_for_row_0_reason_1_, rowIndex, errorText);
dxGridView.CancelUpdateCurrentRow();
tableViewValidator.RefreshRowData();
dxGridView.DeleteRow(rowIndex);
return false;
}
dxGridView.FocusedRowHandle = rowIndex;
//this line is needed for the changes to be 'commited' and the row
//to leave an updating state. http://community.devexpress.com/forums/p/30892/106718.aspx
//unfortunately hard to write a test without exposing/hacking too much
dxGridView.UpdateCurrentRow();
tableViewValidator.RefreshRowData();
return true;
}
private bool SetCellValueInternal(int rowIndex, int columnDisplayIndex, object value)
{
var col = GetDxColumnByDisplayIndex(columnDisplayIndex);
if (CellIsReadOnly(rowIndex, GetColumnByDxColumn(col)))
{
return false;
}
//check if the value can be converted to the column type
object objectValue = value is string
? ConvertStringValueToDataValue(col.AbsoluteIndex, (string) value)
: value;
if (objectValue == null)
{
Log.ErrorFormat(Resources.TableView_SetCellValueInternal_Can_not_set_value_into_cell_0_1_reason_2_, rowIndex, col.AbsoluteIndex, Resources.TableView_SetCellValueInternal_No_conversion_from_string_possible);
return false;
}
string error;
if (!tableViewValidator.ValidateCell(new TableViewCell(rowIndex, GetColumnByDxColumn(col)), objectValue, out error))
{
Log.ErrorFormat(Resources.TableView_SetCellValueInternal_Can_not_set_value_into_cell_0_1_reason_2_, rowIndex, col.AbsoluteIndex, error);
return false;
}
// (Gijs) We are mixing the row handlers and indices throughout this wrapper: should be fixed, as
dxGridView.FocusedRowHandle = rowIndex;
dxGridView.SetRowCellValue(rowIndex, col, objectValue);
return true;
}
private static object ConvertToColumnValue(string cellValue, Type columnType)
{
try
{
return TypeConverter.ConvertValueToTargetType(columnType, cellValue);
}
catch (Exception)
{
if (string.IsNullOrEmpty(cellValue.TrimStart()))
{
if (columnType.IsValueType)
{
return Activator.CreateInstance(columnType);
}
return null;
}
// still try to parse double or float numbers
if (columnType == typeof(double) || columnType == typeof(float))
{
double value;
if (double.TryParse(cellValue, out value))
{
return value;
}
}
Log.WarnFormat(Resources.TableView_ConvertToColumnValue_Unable_to_convert_string_0_to_1_for_paste, cellValue, columnType);
return null;
}
}
#region Public properties
///
/// Specifies whether the edit buttons on the bottom of the table view is shown
///
public bool EditButtons
{
get
{
return dxGridControl.UseEmbeddedNavigator;
}
set
{
dxGridControl.UseEmbeddedNavigator = value;
}
}
public bool AllowDeleteRow
{
get
{
if (bindingSource == null) // lazy initialize
{
bindingSource = new BindingSource
{
DataSource = dxGridView.DataSource
};
}
var tableAllowRemove = dxGridView.OptionsBehavior.AllowDeleteRows;
var dataAllowsRemove = bindingSource.AllowRemove;
// better code, but doesn't work for List (hacked through BindingSource):
// dataAllowsNew = xGridView.DataController != null && dxGridView.DataController.AllowNew;
return (tableAllowRemove == DefaultBoolean.True || tableAllowRemove == DefaultBoolean.Default) &&
dataAllowsRemove;
}
set
{
dxGridView.OptionsBehavior.AllowDeleteRows = value ? DefaultBoolean.True : DefaultBoolean.False;
if (dxGridControl.EmbeddedNavigator != null)
{
dxGridControl.EmbeddedNavigator.Buttons.Remove.Visible = value;
}
}
}
public bool AllowAddNewRow
{
get
{
if (bindingSource == null) // lazy initialize
{
bindingSource = new BindingSource
{
DataSource = dxGridView.DataSource
};
}
var tableAllowNew = dxGridView.OptionsBehavior.AllowAddRows;
var dataAllowsNew = bindingSource.AllowNew;
// better code, but doesn't work for List (hacked through BindingSource):
// dataAllowsNew = xGridView.DataController != null && dxGridView.DataController.AllowNew;
return (tableAllowNew == DefaultBoolean.True || tableAllowNew == DefaultBoolean.Default) &&
dataAllowsNew;
}
set
{
dxGridView.OptionsBehavior.AllowAddRows = value ? DefaultBoolean.True : DefaultBoolean.False;
dxGridView.OptionsView.NewItemRowPosition = value ? NewItemRowPosition.Bottom : NewItemRowPosition.None;
if (dxGridControl.EmbeddedNavigator != null)
{
dxGridControl.EmbeddedNavigator.Buttons.Append.Enabled = value;
}
}
}
public bool AllowColumnSorting
{
get
{
return allowColumnSorting;
}
set
{
if (allowColumnSorting == value)
{
return;
}
allowColumnSorting = value;
foreach (var column in Columns)
{
column.SortingAllowed = allowColumnSorting;
}
}
}
public bool AllowColumnPinning { get; set; }
///
/// Gets or sets whether multiple rows can be selected
///
public bool MultiSelect
{
get
{
return dxGridView.OptionsSelection.MultiSelect;
}
set
{
dxGridView.OptionsSelection.MultiSelect = value;
}
}
public bool IncludeHeadersOnCopy { get; set; }
///
/// Gets a boolean value indicating that the view is being edited or not
///
[Browsable(false)]
public bool IsEditing
{
get
{
return dxGridView.IsEditing || dxGridView.FocusedRowModified;
}
}
///
/// TODO: If RowSelect is true, dxGridView.GetSelectedCells etc will give an empty array.... SelectedRows will stiull work.
///
public bool RowSelect
{
get
{
return dxGridView.OptionsSelection.MultiSelectMode == GridMultiSelectMode.RowSelect;
}
set
{
dxGridView.OptionsSelection.EnableAppearanceFocusedCell = false;
dxGridView.FocusRectStyle = DrawFocusRectStyle.None;
dxGridView.OptionsSelection.MultiSelectMode = (value) ? GridMultiSelectMode.RowSelect : GridMultiSelectMode.CellSelect;
}
}
public bool ShowRowNumbers
{
get
{
return showRowNumbers;
}
set
{
showRowNumbers = value;
UpdateHeaderColumnSize();
}
}
public bool ColumnAutoWidth
{
get
{
return dxGridView.OptionsView.ColumnAutoWidth;
}
set
{
dxGridView.OptionsView.ColumnAutoWidth = value;
}
}
public bool UseCenteredHeaderText
{
get
{
return dxGridView.Appearance.HeaderPanel.TextOptions.HAlignment == HorzAlignment.Center;
}
set
{
dxGridView.Appearance.HeaderPanel.TextOptions.HAlignment = (value)
? HorzAlignment.Center
: HorzAlignment.Default;
}
}
public bool ReadOnly
{
get
{
return !dxGridView.Editable;
}
set
{
dxGridView.OptionsBehavior.Editable = !value;
}
}
public bool MultipleCellEdit { get; set; }
///
/// Determines whether the tableview generates columns when
/// setting data
///
public bool AutoGenerateColumns
{
get
{
return autoGenerateColumns;
}
set
{
autoGenerateColumns = value;
//update gridview behavior
dxGridView.OptionsBehavior.AutoPopulateColumns = value;
}
}
public bool AutoSizeRows
{
get
{
return dxGridView.OptionsView.RowAutoHeight;
}
set
{
dxGridView.OptionsView.RowAutoHeight = value;
}
}
public int HeaderHeigth
{
get
{
return dxGridView.ColumnPanelRowHeight;
}
set
{
if (value > 0)
{
dxGridView.Appearance.HeaderPanel.TextOptions.WordWrap = WordWrap.Wrap;
}
dxGridView.ColumnPanelRowHeight = value;
}
}
public int RowHeight
{
get
{
return dxGridView.RowHeight;
}
set
{
dxGridView.RowHeight = value;
}
}
///
/// Number of rows in the grid excluding the newrow and filtered rows.
///
[Browsable(false)]
public int RowCount
{
get
{
return dxGridView.DataController.VisibleCount;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int FocusedRowIndex
{
get
{
return dxGridView.FocusedRowHandle;
}
set
{
if (dxGridView.FocusedRowHandle != value && dxGridView.FocusedRowHandle != int.MinValue && value > -1)
{
dxGridView.FocusedRowHandle = value;
}
}
}
[Browsable(false)]
public int[] SelectedRowsIndices
{
get
{
return dxGridView.GetSelectedRows();
}
}
[Browsable(false)]
public int[] SelectedColumnsIndices
{
get
{
return selectedCells.Select(c => c.Column.AbsoluteIndex).OrderBy(i => i).Distinct().ToArray();
}
}
///
/// Gets or sets the disabled cell fore color
///
public Color ReadOnlyCellForeColor { get; set; }
///
/// Gets or sets the disabled cell fore color
///
public Color ReadOnlyCellBackColor { get; set; }
///
/// Gets or sets the background color of the invalid cell (value)
///
public Color InvalidCellBackgroundColor { get; set; }
///
/// Returns the focused row (also works if the focused row is new or deleted)
///
[Browsable(false)]
public object CurrentFocusedRowObject
{
get
{
return dxGridView.GetFocusedRow();
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object Data
{
get
{
return dxGridControl.DataSource;
}
set
{
dxGridView.CancelUpdateCurrentRow();
UnSubscribeFromDataSource();
bindingSource = null;
ClearSelection();
// clear before binding enables rebinding to new function; will remove old columns
if (dxGridControl.DataSource != value)
{
// do not explicitly clear columns; this will also remove user added column editors
dxGridControl.DataSource = value;
if (AutoGenerateColumns)
{
dxGridView.PopulateColumns();
}
}
if (value == null)
{
return;
}
SubscribeToDataSource();
BeginInit();
BuildColumnWrappers();
AddEnumCellEditors();
UpdateHeaderColumnSize();
EndInit();
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Image Image { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public TableViewPasteController PasteController { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public ValidationExceptionMode ExceptionMode { get; set; }
[Browsable(false)]
public IList SelectedCells
{
get
{
return selectedCells;
}
}
// avoid serialization of Columns into resx files,
// when something changes in columns designers becomes mad because of broken resx files
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IList Columns
{
get
{
return columns;
}
}
public override ContextMenuStrip ContextMenuStrip
{
get
{
return RowContextMenu;
}
set
{
RowContextMenu = value;
}
}
public ContextMenuStrip RowContextMenu
{
get
{
return dxGridControl.ContextMenuStrip;
}
private set
{
dxGridControl.ContextMenuStrip = value;
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public IList ColumnMenuItems { get; set; }
///
/// Gets or sets the filter to evaluate if the value of the cell is invalid
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func InvalidCellFilter { get; set; }
///
/// Gets or sets the filter that can be used for making cells read-only
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func ReadOnlyCellFilter { get; set; }
///
/// Gets or sets the filter that can be used for styling a cell
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func DisplayCellFilter { get; set; }
///
/// Function for getting or setting unbound column values
/// Parameters : column index, datasource row index, is getter, is setter, value
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func UnboundColumnData { get; set; }
///
/// Gets or sets the row values validator. The validator should return a RowValidationResult
///
/// ?Does not validate before commit to data source?
[Browsable(false)]
public Func RowValidator { get; set; }
///
/// Gets or sets the cell value editor validator. The validator should return true on valid values
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func> InputValidator { get; set; }
///
/// Gets or sets the function that checks if the current selection can be deleted.
/// When null or returning true, deletion is allowed.
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func CanDeleteCurrentSelection { get; set; }
///
/// Gets or sets the function for deleting selected rows (return value indicates if the deleting is handled)
/// When null or returning false the default delete will be executed
///
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public Func RowDeleteHandler { get; set; }
public bool IsEndEditOnEnterKey { get; set; }
///
/// Enable filtering for all columns
///
public bool AllowColumnFiltering
{
get
{
return Columns.OfType().Any(c => c.FilteringAllowed);
}
set
{
foreach (var column in Columns.OfType())
{
column.FilteringAllowed = value;
}
}
}
public ViewInfo ViewInfo { get; set; }
#endregion
#region Public functions
public new void ResetBindings()
{
dxGridControl.ResetBindings();
if (AutoGenerateColumns)
{
dxGridView.PopulateColumns();
BuildColumnWrappers();
BestFitColumns();
}
base.ResetBindings();
}
public void EnsureVisible(object item) {}
///
/// Function to check if the current focused row is a
/// new row (row that's not committed to the datasource)
///
/// If the focused row is a new row
public bool OnNewRow()
{
return dxGridView.FocusedRowHandle.Equals(GridControl.NewItemRowHandle);
}
///
/// Clear selected rows
///
public void ClearSelection()
{
dxGridView.ClearSelection();
}
///
/// Allows to fit all columns to their contents.
///
public void BestFitColumns(bool useOnlyFirstWordOfHeader = false)
{
dxGridView.BestFitMaxRowCount = Columns.Count > 50 ? 10 : 50;
if (useOnlyFirstWordOfHeader)
{
BestFitColumnsWithOnlyFirstWordOfHeader();
}
else
{
dxGridView.BestFitColumns();
}
}
public int GetRowIndexByDataSourceIndex(int datarowIndex)
{
return dxGridView.GetRowHandle(datarowIndex);
}
///
/// Sets a display filter on a column. Note that it filters values on it's display text rather than the actual value.
///
/// The name of the column to apply a display filter to
/// The SQL-like filter expression to apply
public void SetColumnFilter(string columnName, string filter)
{
if (filter == string.Empty)
{
// Remove the filter rather than add or change it
dxGridView.ActiveFilter.Remove(dxGridView.Columns[columnName]);
return;
}
var columnFilterInfo = new ColumnFilterInfo(filter);
dxGridView.ActiveFilter.Add(dxGridView.Columns[columnName], columnFilterInfo);
}
public void BeginInit()
{
dxGridView.BeginUpdate();
dxGridControl.BeginUpdate();
}
public void EndInit()
{
dxGridControl.EndUpdate();
dxGridView.EndUpdate();
}
public TableViewColumn GetColumnByName(string columnName)
{
return Columns.FirstOrDefault(c => c.Name == columnName);
}
public void SelectRow(int index, bool clearPreviousSelection = true)
{
SelectRows(new[]
{
index
}, clearPreviousSelection);
}
public void SelectRows(int[] indices, bool clearPreviousSelection = true)
{
dxGridView.BeginSelection();
if (clearPreviousSelection)
{
dxGridView.ClearSelection(); //clear any previous selection
}
dxGridView.HideEditor(); //close any open editor
for (int i = 0; i < indices.Length; i++)
{
if (i == 0)
{
dxGridView.SelectionChanged -= DxGridViewSelectionChanged;
}
if (i == indices.Length - 1)
{
dxGridView.SelectionChanged += DxGridViewSelectionChanged;
}
dxGridView.SelectRow(indices[i]);
}
dxGridView.EndSelection();
}
///
/// Returns the index of the data source record which the specified row handle corresponds to.
///
/// The visualized, zero-based row-index in the table
///
/// An integer value representing the zero-based index of the data record to which the specified row handle corresponds
///
public int GetDataSourceIndexByRowIndex(int rowIndex)
{
RefreshIfRequired();
return dxGridView.GetDataSourceRowIndex(rowIndex);
}
private void RefreshIfRequired()
{
if (refreshRequired)
{
RefreshData();
refreshRequired = false;
}
}
public void RefreshData()
{
dxGridControl.RefreshDataSource();
dxGridView.LayoutChanged();
}
public void ScheduleRefresh()
{
refreshRequired = true;
}
public bool CellIsReadOnly(int rowHandle, TableViewColumn column)
{
// Tableview readonly?
if (ReadOnly)
{
return true;
}
// Column does not exist or cannot be found (not visible for example)
if (column == null)
{
return true;
}
// Column readonly?
if (column.ReadOnly)
{
return true;
}
// Cell readonly?
if (ReadOnlyCellFilter != null)
{
return ReadOnlyCellFilter(new TableViewCell(rowHandle, column));
}
// Cell is not read only
return false;
}
///
/// Assign a certain value to cell.
///
/// Display index of the row
/// Display index of the column
/// String value to be set
public bool SetCellValue(int rowIndex, int columnIndex, object value)
{
tableViewValidator.AutoValidation = false;
try
{
if (!SetCellValueInternal(rowIndex, columnIndex, value))
{
return false;
}
if (!ValidateAndCommitRow(rowIndex))
{
return false;
}
}
finally
{
tableViewValidator.AutoValidation = true;
}
return true;
}
public bool SetRowCellValues(int rowIndex, int columnDisplayStartIndex, object[] cellValues)
{
tableViewValidator.AutoValidation = false;
var success = true;
try
{
for (int i = 0; i < cellValues.Length; i++)
{
if (CellIsReadOnly(rowIndex, GetColumnByDisplayIndex(i + columnDisplayStartIndex))) // skip readonly cells, does not affect success.
{
continue;
}
success &= SetCellValueInternal(rowIndex, i + columnDisplayStartIndex, cellValues[i]);
}
if (!success)
{
return false;
}
success = ValidateAndCommitRow(rowIndex);
}
finally
{
tableViewValidator.AutoValidation = true;
}
return success;
}
public object GetRowObjectAt(int rowIndex)
{
return dxGridView.RowCount > rowIndex && rowIndex >= 0 ? dxGridView.GetRow(rowIndex) : null;
}
///
/// Gets value of a certain cell.
///
/// Index of the row
/// Absolute index of the column
public object GetCellValue(int rowIndex, int absoluteColumnIndex)
{
return dxGridView.GetRowCellValue(rowIndex, dxGridView.Columns[absoluteColumnIndex]);
}
///
/// Gets value of a certain cell.
///
public object GetCellValue(TableViewCell cell)
{
return GetCellValue(cell.RowIndex, cell.Column.AbsoluteIndex);
}
///
/// Selects cells in a square described by top,left -> bottom,right
///
///
///
///
///
///
public void SelectCells(int top, int left, int bottom, int right, bool clearOldSelection = true)
{
// dxGridControl.SuspendLayout();
dxGridView.BeginSelection();
if (RowSelect)
{
throw new InvalidOperationException(Resources.TableView_SelectCells_Unable_to_select_cells_when_tableView_has_RowSelect_enabled_Use_SelectRow_instead);
}
if (clearOldSelection)
{
selectedCells.Clear();
}
for (int y = top; y <= bottom; y++)
{
for (int x = left; x <= right; x++)
{
selectedCells.Add(new TableViewCell(y, GetColumnByDisplayIndex(x)));
}
}
dxGridView.EndSelection();
//dxGridControl.ResumeLayout();
}
///
/// Pastes clipboard contens into current selection
///
public void PasteClipboardContents()
{
isPasting = true;
PasteController.PasteClipboardContents();
isPasting = false;
}
///
/// Copy original (not the display) values to the clipboard as strings
///
public void CopySelectionToClipboard()
{
var sb = new StringBuilder();
var areAllCellsSelected = dxGridView.GetSelectedCells().Length == (dxGridView.DataRowCount*dxGridView.VisibleColumns.Count);
if (areAllCellsSelected && IncludeHeadersOnCopy)
{
CopyHeader(sb);
}
var selectedGridCells = dxGridView.GetSelectedCells();
var rowIndex = -1;
foreach (var cell in selectedGridCells)
{
if (rowIndex != cell.RowHandle)
{
if (rowIndex != -1)
{
sb.AppendLine();
}
rowIndex = cell.RowHandle;
}
else
{
sb.Append("\t");
}
sb.Append(dxGridView.GetRowCellValue(cell.RowHandle, cell.Column));
}
if (rowIndex != -1)
{
sb.AppendLine();
}
Clipboard.Clear();
Clipboard.SetData(DataFormats.Text, sb.ToString());
}
private void CopyHeader(StringBuilder sb)
{
var header = "";
for (var i = 0; i < dxGridView.Columns.Count; i++)
{
if (header != "")
{
header += "\t";
}
header += dxGridView.Columns[i].GetTextCaption().Replace("\n", " ");
}
sb.AppendLine(header);
}
public void SetFocus(int rowIndex, int columnIndex)
{
var column = GetDxColumnByDisplayIndex(columnIndex);
dxGridView.FocusedRowHandle = rowIndex;
dxGridView.FocusedColumn = column;
//this is needed to get the focus visible
dxGridView.ShowEditor();
dxGridView.HideEditor();
}
public void DeleteCurrentSelection()
{
if (ReadOnly)
{
return;
}
dxGridView.CancelUpdateCurrentRow(); // cancel any open changes (as that may trigger validation)
// Nothing to delete?
if (dxGridView.GetSelectedRows().Length == 0)
{
return;
}
// Is deletion allowed?
if (CanDeleteCurrentSelection != null && !CanDeleteCurrentSelection())
{
return;
}
//delete rows in case entire rows are selected
var groupBy = SelectedCells.GroupBy(c => c.RowIndex);
var count = Columns.Count(c => c.Visible);
if (AllowDeleteRow && (RowSelect || groupBy.All(g => g.Count() == count)))
{
if (RowDeleteHandler == null || !RowDeleteHandler())
{
dxGridView.DeleteSelectedRows();
}
return;
}
var selectedGridCells = SelectedCells.ToList();
foreach (var c in selectedGridCells)
{
var defaultValue = c.Column.DefaultValue ??
TypeUtils.GetDefaultValue(c.Column.ColumnType);
SetCellValue(c.RowIndex, c.Column.DisplayIndex, Convert.ToString(defaultValue));
}
}
public void AddColumn(string dataSourcePropertyName, string columnCaption, bool readOnly = false, int width = 100, Type columnType = null, string displayFormat = null)
{
var dxColumn = dxGridView.Columns.AddField(dataSourcePropertyName);
dxColumn.Caption = columnCaption;
dxColumn.VisibleIndex = dxGridView.Columns.Count - 1;
dxColumn.OptionsColumn.ReadOnly = readOnly;
dxColumn.Width = width;
var column = new TableViewColumn(dxGridView, dxGridControl, dxColumn, this, false);
if (displayFormat != null)
{
column.DisplayFormat = displayFormat;
}
Columns.Add(column);
}
///
/// Adds an unbound column to the table. (Use to set values for the column)
///
/// Name of the column
/// Type of the data the column is going to display
/// Index of the column
/// Editor to use for editing the values of this column
public void AddUnboundColumn(string columnName, Type columnType, int index = -1, Editors.ITypeEditor editor = null)
{
var unbColumn = dxGridView.Columns.AddField(columnName);
var column = new TableViewColumn(dxGridView, dxGridControl, unbColumn, this, true);
Columns.Add(column);
unbColumn.VisibleIndex = index != -1 ? index : dxGridView.Columns.Count;
if (columnType == typeof(double))
{
unbColumn.UnboundType = UnboundColumnType.Decimal;
unbColumn.DisplayFormat.FormatType = FormatType.Numeric;
}
else if (columnType == typeof(int))
{
unbColumn.UnboundType = UnboundColumnType.Integer;
unbColumn.DisplayFormat.FormatType = FormatType.Numeric;
}
else if (columnType == typeof(string))
{
unbColumn.UnboundType = UnboundColumnType.String;
unbColumn.DisplayFormat.FormatType = FormatType.None;
}
else if (columnType.IsEnum)
{
unbColumn.UnboundType = UnboundColumnType.Object;
}
else if (columnType == typeof(DateTime))
{
unbColumn.UnboundType = UnboundColumnType.DateTime;
unbColumn.DisplayFormat.FormatType = FormatType.Custom;
}
else
{
throw new ArgumentException(string.Format(Resources.TableView_AddUnboundColumn_Unbound_columns_of_type_0_not_supported, columnType));
}
if (editor != null)
{
column.Editor = editor;
}
dxGridView.CustomUnboundColumnData -= DxGridViewCustomUnboundColumnData;
dxGridView.CustomUnboundColumnData += DxGridViewCustomUnboundColumnData;
}
public string GetCellDisplayText(int rowIndex, int absoluteColumnIndex)
{
return dxGridView.GetDisplayTextByColumnValue(dxGridView.Columns[absoluteColumnIndex], GetCellValue(rowIndex, absoluteColumnIndex));
}
#endregion
#region Public events
public event EventHandler FocusedRowChanged;
public event EventHandler SelectionChanged;
public event EventHandler> CellChanged;
public event EventHandler> ColumnFilterChanged;
#endregion
#region Internal functions
///
/// Returns true when any column is sorted
///
internal bool IsSorted()
{
return Columns.Any(c => c.SortOrder != SortOrder.None);
}
internal TableViewCell GetFocusedCell()
{
//no selection
if (dxGridView.FocusedColumn == null)
{
return null;
}
//no row selected
if (dxGridView.FocusedRowHandle == int.MinValue)
{
return null;
}
//'new' row..select the bottom
if (dxGridView.FocusedRowHandle == (int.MinValue + 1))
{
var rowIndex = Math.Max(0, dxGridView.RowCount - 1);
return new TableViewCell(rowIndex, GetColumnByDxColumn(dxGridView.FocusedColumn));
}
return new TableViewCell(dxGridView.FocusedRowHandle, GetColumnByDxColumn(dxGridView.FocusedColumn));
}
internal void SetColumnError(TableViewColumn tableColumn, string errorText)
{
var dxGridColumn = tableColumn != null ? dxGridView.Columns[tableColumn.AbsoluteIndex] : null;
dxGridView.SetColumnError(dxGridColumn, errorText);
}
internal void AddNewRowToDataSource()
{
if (bindingSource == null)
{
bindingSource = new BindingSource
{
DataSource = dxGridView.DataSource
};
}
if (!bindingSource.AllowNew)
{
Log.Debug(Resources.TableView_AddNewRowToDataSource_Adding_rows_to_this_table_is_not_allowed);
return;
}
bindingSource.AddNew();
//end edit is needed when we bind to datatable
if (bindingSource.DataSource is DataTable || bindingSource.DataSource is DataView)
{
bindingSource.EndEdit();
}
}
internal TableViewColumn GetColumnByDisplayIndex(int i)
{
return Columns.FirstOrDefault(c => c.DisplayIndex == i);
}
#endregion
#region Gridview event handlers
private void EmbeddedNavigatorButtonClick(object sender, NavigatorButtonClickEventArgs e)
{
if (e.Button != dxGridControl.EmbeddedNavigator.Buttons.Remove)
{
return;
}
// reroute delete event to "our" delete function
e.Handled = true;
DeleteCurrentSelection();
}
///
/// Do keyboard cursor interaction like in Excel (StackOverflow: (c)Jakob Möllås)
///
///
///
private void DxGridControlEditorKeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter && UserIsHoldingDownControlForCellFill())
{
// grab selected cells here because changing the first value may cause the
// data source to refresh and the selection to be lost
cellsToFill = dxGridView.GetSelectedCells();
return;
}
if (e.KeyData != Keys.Left && e.KeyData != Keys.Right)
{
return;
}
var gridControl = sender as GridControl;
if (gridControl == null)
{
return;
}
var view = gridControl.FocusedView as GridView;
if (view == null)
{
return;
}
var textEdit = view.ActiveEditor as TextEdit;
if (textEdit == null)
{
return;
}
var left = e.KeyData == Keys.Left;
var right = e.KeyData == Keys.Right;
// Handle initial case - everything selected in control
if ((left || right) &&
textEdit.SelectionLength == textEdit.Text.Length &&
textEdit.SelectionStart == 0)
{
textEdit.SelectionStart = left ? 0 : textEdit.Text.Length; //minor adjustment
textEdit.SelectionLength = 0;
e.Handled = true;
return;
}
// Handle left & rightmost positions (prevent focus change)
e.Handled = left && textEdit.SelectionStart == 0 ||
right && textEdit.SelectionStart == textEdit.Text.Length;
}
private void DxGridViewDoubleClick(object sender, EventArgs e)
{
OnDoubleClick(e);
}
private void DxGridViewCustomDrawCell(object sender, RowCellCustomDrawEventArgs e)
{
var tableViewColumn = GetColumnByDxColumn(e.Column);
var buttonControl = tableViewColumn.Editor as ButtonTypeEditor;
if (buttonControl == null || !buttonControl.HideOnReadOnly)
{
return;
}
var cellReadOnly = CellIsReadOnly(e.RowHandle, tableViewColumn);
if (!cellReadOnly)
{
return;
}
var buttonEditViewInfo = ((GridCellInfo) e.Cell).ViewInfo as ButtonEditViewInfo;
if ((buttonEditViewInfo == null) || (buttonEditViewInfo.RightButtons.Count != 1))
{
return;
}
e.Appearance.FillRectangle(new GraphicsCache(e.Graphics), e.Bounds);
e.Handled = true;
}
private void DxGridViewColumnFilterChanged(object sender, EventArgs e)
{
if (ColumnFilterChanged == null)
{
return;
}
var selectedColumn = Columns.OfType()
.FirstOrDefault(c => c.DxColumn == dxGridView.FocusedColumn);
ColumnFilterChanged(sender, new EventArgs(selectedColumn));
}
private void DxGridViewCustomDrawRowIndicator(object sender, RowIndicatorCustomDrawEventArgs e)
{
if (!showRowNumbers)
{
return;
}
// check whether the indicator cell belongs to a data row
if (e.Info.IsRowIndicator && e.RowHandle >= 0)
{
e.Info.DisplayText = (e.RowHandle + 1).ToString(CultureInfo.InvariantCulture);
e.Info.ImageIndex = -1;
e.Info.Appearance.TextOptions.HAlignment = HorzAlignment.Far;
if (FocusedRowIndex == e.RowHandle)
{
e.Info.ImageIndex = 0;
}
}
}
private void DxGridViewShowDxGridMenu(object sender, GridMenuEventArgs e)
{
bool first = true;
if (e.MenuType == GridMenuType.Column)
{
TableViewColumn tableViewColumn = null;
if (e.HitInfo.Column != null) //on a column
{
tableViewColumn = Columns.FirstOrDefault(c => c.Caption == e.HitInfo.Column.GetTextCaption());
}
UpdateColumnHeaderMenu(e, tableViewColumn);
foreach (var menuItem in ColumnMenuItems.Where(menuItem => menuItem.ShouldShow(tableViewColumn)))
{
if (first)
{
menuItem.InternalItem.BeginGroup = true;
first = false;
}
else
{
menuItem.InternalItem.BeginGroup = false;
}
e.Menu.Items.Add(menuItem.InternalItem);
}
}
}
private void DxGridView2RowCellStyle(object sender, RowCellStyleEventArgs e)
{
var selected = ((!RowSelect && dxGridView.IsCellSelected(e.RowHandle, e.Column)) ||
(RowSelect && dxGridView.IsRowSelected(e.RowHandle)));
if (DisplayCellFilter != null)
{
var tableViewCellStyle = new TableViewCellStyle(e.RowHandle, GetColumnByDxColumn(e.Column), selected)
{
ForeColor = e.Appearance.ForeColor,
BackColor = e.Appearance.BackColor
};
if (DisplayCellFilter(tableViewCellStyle))
{
e.Appearance.ForeColor = tableViewCellStyle.ForeColor;
e.Appearance.BackColor = tableViewCellStyle.BackColor;
return;
}
}
if (selected)
{
e.Appearance.ForeColor = Color.White;
e.Appearance.BackColor = SystemColors.Highlight;
return;
}
var isReadOnly = CellIsReadOnly(e.RowHandle, GetColumnByDxColumn(e.Column));
if (isReadOnly)
{
e.Appearance.ForeColor = ReadOnlyCellForeColor;
e.Appearance.BackColor = ReadOnlyCellBackColor;
}
if (InvalidCellFilter == null)
{
return;
}
if (InvalidCellFilter(new TableViewCell(e.RowHandle, GetColumnByDxColumn(e.Column))))
{
e.Appearance.BackColor = InvalidCellBackgroundColor;
}
}
private void DxGridViewShownEditor(object sender, EventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
var textEditor = view.ActiveEditor as TextEdit;
if (f2Pressed && textEditor != null && textEditor.IsEditorActive)
{
textEditor.SelectionStart = textEditor.Text.Length;
}
var buttonEditor = view.ActiveEditor as ButtonEdit;
if (buttonEditor != null)
{
buttonEditor.PerformClick(null);
}
dxGridControl.EmbeddedNavigator.Buttons.Append.Enabled = AllowAddNewRow && !OnNewRow();
f2Pressed = false;
}
private void DxGridViewShowingEditor(object sender, CancelEventArgs e)
{
var view = sender as GridView;
if (view == null)
{
return;
}
e.Cancel = CellIsReadOnly(view.FocusedRowHandle, GetColumnByDxColumn(view.FocusedColumn));
}
private void DxGridControlProcessDxGridKey(object sender, KeyEventArgs e)
{
f2Pressed = e.KeyCode == Keys.F2;
//TODO: get to switch like stament
if (((e.Shift) && (e.KeyCode == Keys.Insert)) ||
((e.Control) && (e.KeyCode == Keys.V)))
{
//paste the glipboard if we are not editing a cell
if ((dxGridView.OptionsBehavior.Editable) && (dxGridView.State != GridState.Editing))
{
PasteClipboardContents();
//we need to suppres ctrl + v for being passing to child
//controls. Others wise the column goes into an editing state
e.SuppressKeyPress = true;
e.Handled = true;
}
}
if (((e.Control) && (e.KeyCode == Keys.Insert)) ||
((e.Control) && (e.KeyCode == Keys.C)))
{
CopySelectionToClipboard();
e.Handled = true; //prevent XtraGrid from doing another copy resulting in an exception
}
if ((e.KeyCode == Keys.Delete) && (dxGridView.State == GridState.Normal))
{
DeleteCurrentSelection();
}
if (e.KeyCode == Keys.Enter && IsEndEditOnEnterKey && (dxGridView.IsEditing || OnNewRow()))
{
dxGridControl.EmbeddedNavigator.Buttons.DoClick(dxGridControl.EmbeddedNavigator.Buttons.EndEdit);
e.Handled = true;
e.SuppressKeyPress = true;
}
}
private void DxGridViewFocusedColumnChanged(object sender, FocusedColumnChangedEventArgs e)
{
if (e.FocusedColumn != null && !dxGridView.Columns.Contains(e.FocusedColumn))
{
// This is an awkward situation that can arise when a user clicks in the grid somewhere, after entering a value without Enter.
dxGridView.FocusedColumn = null;
}
ShowEditorIfRowSelect();
}
private void DxGridViewClick(object sender, EventArgs e)
{
var gridHitInfo = dxGridView.CalcHitInfo(PointToClient(MousePosition));
if (gridHitInfo.Column != null && gridHitInfo.Column.ColumnEdit is RepositoryItemButtonEdit)
{
dxGridView.ShowEditor(); // Reduces the number of clicks needed to actually click buttons by one
}
if (gridHitInfo.InColumn && (!gridHitInfo.InColumnPanel && RowSelect) && gridHitInfo.Column != null)
{
var columnIndex = gridHitInfo.Column.VisibleIndex;
if (((ModifierKeys & Keys.Control) == Keys.Control))
{
if (!DeselectSelectCells(0, columnIndex, dxGridView.RowCount - 1, columnIndex))
{
SelectCells(0, columnIndex, dxGridView.RowCount - 1, columnIndex, false);
}
}
else
{
SelectCells(0, columnIndex, dxGridView.RowCount - 1, columnIndex, true);
}
}
//bubble click..
OnClick(e);
}
///
/// Called when a cell value is validated and the new value is set to the current cell.
/// If the user presses CTRL+ENTER the new value is copied to all selected cells. This
/// mimics the behaviour in Excel XP.
/// TODO: when the user presses cancel after the CTRL+ENTER only the current cell is reset.
///
private void DxGridViewCellValueChanged(object sender, CellValueChangedEventArgs e)
{
if (UserIsHoldingDownControlForCellFill())
{
// temporarily disable value changed event
dxGridView.CellValueChanged -= DxGridViewCellValueChanged;
try
{
if (cellsToFill == null)
{
return;
}
foreach (GridCell gridcell in cellsToFill)
{
// only set new value to cell of same type
// todo add support for mixing double, float and int?
if (gridcell.Column.ColumnType == e.Value.GetType())
{
dxGridView.SetRowCellValue(gridcell.RowHandle, gridcell.Column, e.Value);
}
}
}
finally
{
dxGridView.CellValueChanged += DxGridViewCellValueChanged;
cellsToFill = null;
}
}
if (CellChanged != null)
{
CellChanged(this, new EventArgs(new TableViewCell(e.RowHandle, GetColumnByDxColumn(e.Column))));
}
}
private bool UserIsHoldingDownControlForCellFill()
{
return MultipleCellEdit && ModifierKeys == Keys.Control && !isPasting;
}
private void DxGridViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!isSelectionChanging)
{
isSelectionChanging = true;
}
UpdateSelectionFromGridControl();
}
private void DxGridViewFocusedRowChanged(object sender, FocusedRowChangedEventArgs e)
{
if (FocusedRowChanged != null)
{
FocusedRowChanged(sender, EventArgs.Empty);
}
}
private void DxGridViewCustomUnboundColumnData(object sender, CustomColumnDataEventArgs e)
{
if (UnboundColumnData == null)
{
return;
}
var result = UnboundColumnData(e.Column.AbsoluteIndex, e.ListSourceRowIndex, e.IsGetData, e.IsSetData, e.Value);
if (result != null)
{
e.Value = result;
}
}
private void DxGridViewMouseEnter(object sender, EventArgs e)
{
OnMouseEnter(e);
}
private void DxGridViewMouseDown(object sender, MouseEventArgs e)
{
// Check edit single click when in cell select mode
// http://www1.devexpress.com/Support/Center/p/Q144046.aspx
if ((ModifierKeys & Keys.Control) != Keys.Control && (ModifierKeys & Keys.Shift) != Keys.Shift)
{
var hi = dxGridView.CalcHitInfo(e.Location);
if (hi.InRowCell && hi.Column.RealColumnEdit.GetType() == typeof(RepositoryItemCheckEdit))
{
dxGridView.FocusedRowHandle = hi.RowHandle;
dxGridView.FocusedColumn = hi.Column;
dxGridView.ShowEditor();
// get active editor or create one (when cell is readonly and no ActiveEditor is set)
var checkEdit = (dxGridView.ActiveEditor ?? hi.Column.RealColumnEdit.CreateEditor()) as CheckEdit;
if (checkEdit != null)
{
var checkInfo = (CheckEditViewInfo) checkEdit.GetViewInfo();
var glyphRect = checkInfo.CheckInfo.GlyphRect;
var viewInfo = dxGridView.GetViewInfo() as GridViewInfo;
if (viewInfo != null)
{
var gridGlyphRect = new Rectangle(viewInfo.GetGridCellInfo(hi).Bounds.X + glyphRect.X,
viewInfo.GetGridCellInfo(hi).Bounds.Y + glyphRect.Y,
glyphRect.Width,
glyphRect.Height);
dxGridView.ClearSelection();
if (!gridGlyphRect.Contains(e.Location))
{
dxGridView.CloseEditor();
if (!RowSelect)
{
if (!dxGridView.IsCellSelected(hi.RowHandle, hi.Column))
{
dxGridView.SelectCell(hi.RowHandle, hi.Column);
}
else
{
dxGridView.UnselectCell(hi.RowHandle, hi.Column);
}
}
}
else
{
checkEdit.Checked = !checkEdit.Checked;
dxGridView.CloseEditor();
}
if (RowSelect)
{
dxGridView.SelectRow(hi.RowHandle);
}
}
}
var dxMouseEventArgs = e as DXMouseEventArgs;
if (dxMouseEventArgs != null)
{
dxMouseEventArgs.Handled = true;
}
}
}
HandleHeaderPanelButton(sender, e);
OnMouseDown(e);
}
private void DxGridViewInvalidRowException(object sender, InvalidRowExceptionEventArgs e)
{
switch (ExceptionMode)
{
case ValidationExceptionMode.Ignore:
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.Ignore;
break;
case ValidationExceptionMode.NoAction:
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.NoAction;
break;
case ValidationExceptionMode.DisplayError:
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.DisplayError;
break;
case ValidationExceptionMode.ThrowException:
e.ExceptionMode = DevExpress.XtraEditors.Controls.ExceptionMode.ThrowException;
break;
}
}
private static void DxGridControlPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
e.IsInputKey = true; //do no use special processing
}
}
private void DxGridViewHiddenEditor(object sender, EventArgs e)
{
dxGridControl.EmbeddedNavigator.Buttons.Append.Enabled = AllowAddNewRow;
}
private void OnDxGridViewOnFocusedRowChanged(object o, FocusedRowChangedEventArgs args)
{
DxGridViewFocusedRowChanged(o, args);
}
#endregion
#region Initialization functions
private void ConfigureContextMenu()
{
var btnCopy = new ToolStripMenuItem
{
Name = "btnCopy", Image = Resources.CopyHS, Size = new Size(116, 22), Text = Resources.TableView_ConfigureContextMenu_Copy, Tag = "btnCopy"
};
var btnPaste = new ToolStripMenuItem
{
Name = "btnPaste", Image = Resources.PasteHS, Size = new Size(116, 22), Text = Resources.TableView_ConfigureContextMenu_Paste, Tag = "btnPaste"
};
var btnDelete = new ToolStripMenuItem
{
Name = "btnDelete", Image = Resources.DeleteHS1, Size = new Size(116, 22), Text = Resources.TableView_ConfigureContextMenu_Delete, Tag = "btnDelete"
};
btnCopy.Click += delegate { CopySelectionToClipboard(); };
btnPaste.Click += delegate { PasteClipboardContents(); };
btnDelete.Click += delegate { DeleteCurrentSelection(); };
var viewContextMenu = new ContextMenuStrip();
viewContextMenu.Items.AddRange(new ToolStripItem[]
{
btnCopy,
btnPaste,
btnDelete
});
RowContextMenu = viewContextMenu;
}
private void CreateRefreshTimer()
{
refreshTimer = new Timer();
refreshTimer.Tick += OnRefreshTimerOnTick;
refreshTimer.Interval = 300;
refreshTimer.Enabled = true;
refreshTimer.Start();
}
private void OnRefreshTimerOnTick(object sender, EventArgs e)
{
RefreshIfRequired();
}
private void SubscribeEvents()
{
// mouse events
dxGridView.Click += DxGridViewClick;
dxGridView.DoubleClick += DxGridViewDoubleClick;
dxGridView.MouseEnter += DxGridViewMouseEnter;
dxGridView.MouseDown += DxGridViewMouseDown;
dxGridView.InvalidRowException += DxGridViewInvalidRowException;
// keyboard events
dxGridControl.PreviewKeyDown += DxGridControlPreviewKeyDown;
dxGridControl.ProcessGridKey += DxGridControlProcessDxGridKey;
// change of focus
dxGridView.FocusedRowChanged += OnDxGridViewOnFocusedRowChanged;
dxGridView.FocusedRowChanged += tableViewValidator.RowLostFocus;
dxGridView.FocusedColumnChanged += DxGridViewFocusedColumnChanged;
// value changes
dxGridView.CellValueChanged += tableViewValidator.OnCellValueChanged;
dxGridView.CellValueChanged += DxGridViewCellValueChanged;
// filtering events
dxGridView.ColumnFilterChanged += DxGridViewColumnFilterChanged;
// drawing events
dxGridView.CustomDrawRowIndicator += DxGridViewCustomDrawRowIndicator;
dxGridView.CustomDrawCell += DxGridViewCustomDrawCell;
dxGridView.RowCellStyle += DxGridView2RowCellStyle;
// editor events
dxGridView.ShowingEditor += DxGridViewShowingEditor;
dxGridView.ShownEditor += DxGridViewShownEditor;
dxGridView.HiddenEditor += tableViewValidator.HiddenEditor;
dxGridView.HiddenEditor += DxGridViewHiddenEditor;
dxGridView.ValidatingEditor += (s, e) =>
{
var cell = new TableViewCell(FocusedRowIndex, GetColumnByDxColumn(dxGridView.FocusedColumn));
tableViewValidator.OnValidateCell(cell, e);
};
dxGridControl.EditorKeyDown += DxGridControlEditorKeyDown;
// selection events
dxGridView.SelectionChanged += DxGridViewSelectionChanged;
selectedCells.CollectionChanged += SelectedCellsCollectionChanged;
dxGridView.ShowGridMenu += DxGridViewShowDxGridMenu;
dxGridView.ValidateRow += tableViewValidator.OnValidateRow;
dxGridControl.EmbeddedNavigator.ButtonClick += EmbeddedNavigatorButtonClick;
PasteController.PasteFailed += CopyPasteControllerPasteFailed;
columns.CollectionChanged += ColumnsOnCollectionChanged;
}
#endregion
}
}