|
@@ -241,6 +241,12 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
_tree.FilterLevel = FilterLevel.Extended;
|
|
|
_tree.SelectionForeground = DynamicGridUtils.SelectionForeground;
|
|
|
_tree.SelectionBackground = DynamicGridUtils.SelectionBackground;
|
|
|
+
|
|
|
+ _tree.EditTrigger = EditTrigger.OnTap;
|
|
|
+ _tree.CurrentCellBeginEdit += _tree_CurrentCellBeginEdit;
|
|
|
+ _tree.CurrentCellEndEdit += _tree_CurrentCellEndEdit;
|
|
|
+ _tree.CurrentCellDropDownSelectionChanged += _tree_CurrentCellDropDownSelectionChanged;
|
|
|
+ _tree.PreviewKeyUp += _tree_PreviewKeyUp;
|
|
|
|
|
|
_tree.ColumnSizer = TreeColumnSizer.None;
|
|
|
_tree.RowHeight = 30D;
|
|
@@ -276,12 +282,17 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
|
|
|
#region Input
|
|
|
|
|
|
- private CoreRow? GetRowFromIndex(int rowIndex)
|
|
|
+ private CoreTreeNode? GetNodeFromIndex(int rowIndex)
|
|
|
{
|
|
|
// Syncfusion has given us the row index, so it also will give us the correct row, after sorting.
|
|
|
// Hence, here we use the syncfusion DataGrid.GetRecordAtRowIndex, which *should* always return a DataRowView.
|
|
|
var row = _tree.GetNodeAtRowIndex(rowIndex);
|
|
|
- return MapRow((row.Item as CoreTreeNode)?.Row);
|
|
|
+ return row.Item as CoreTreeNode;
|
|
|
+ }
|
|
|
+
|
|
|
+ private CoreRow? GetRowFromIndex(int rowIndex)
|
|
|
+ {
|
|
|
+ return MapRow(GetNodeFromIndex(rowIndex)?.Row);
|
|
|
}
|
|
|
|
|
|
private void _tree_CellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
|
|
@@ -493,6 +504,16 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
{
|
|
|
ColumnsMenu.Visibility = Parent.Options.SelectColumns ? Visibility.Visible : Visibility.Hidden;
|
|
|
|
|
|
+ var allowEditing = Parent.IsDirectEditMode();
|
|
|
+ var reloadColumns = false;
|
|
|
+
|
|
|
+ if (_tree.AllowEditing != allowEditing)
|
|
|
+ {
|
|
|
+ _tree.NavigationMode = allowEditing ? NavigationMode.Cell : NavigationMode.Row;
|
|
|
+ _tree.AllowEditing = allowEditing;
|
|
|
+ reloadColumns = true;
|
|
|
+ }
|
|
|
+
|
|
|
_tree.AllowFiltering = Parent.Options.FilterRows;
|
|
|
|
|
|
if (Parent.Options.DragSource)
|
|
@@ -515,7 +536,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
_tree.AllowDrop = Parent.Options.DragTarget;
|
|
|
_tree.SelectionMode = Parent.Options.MultiSelect ? GridSelectionMode.Extended : GridSelectionMode.Single;
|
|
|
|
|
|
- return false;
|
|
|
+ return reloadColumns;
|
|
|
}
|
|
|
|
|
|
private void _tree_CellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
|
|
@@ -1040,6 +1061,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
public CoreTreeNodes Nodes { get; set; }
|
|
|
|
|
|
private CoreTable? _innerTable;
|
|
|
+ private bool _invalidating = false;
|
|
|
|
|
|
public void BeforeRefresh()
|
|
|
{
|
|
@@ -1055,6 +1077,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
_innerTable.LoadColumns(data.Columns);
|
|
|
|
|
|
for (var i = 0; i < ActionColumns.Count; i++)
|
|
|
+ {
|
|
|
_innerTable.Columns.Add(
|
|
|
new CoreColumn
|
|
|
{
|
|
@@ -1063,6 +1086,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
? typeof(BitmapImage)
|
|
|
: typeof(String)
|
|
|
});
|
|
|
+ }
|
|
|
|
|
|
foreach (var row in data.Rows)
|
|
|
{
|
|
@@ -1074,6 +1098,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
var _parent = row.Get<Guid>(ParentColumn.Property);
|
|
|
nodes.Add(_id, _parent, newRow);
|
|
|
}
|
|
|
+ nodes.ColumnChanged += Nodes_ColumnChanged;
|
|
|
Nodes = nodes;
|
|
|
_tree.ItemsSource = nodes.Nodes;
|
|
|
|
|
@@ -1086,6 +1111,7 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
public void AddPage(IEnumerable<CoreRow> page)
|
|
|
{
|
|
|
if (_innerTable is null) return;
|
|
|
+ _invalidating = true;
|
|
|
|
|
|
foreach(var row in page)
|
|
|
{
|
|
@@ -1100,6 +1126,8 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
|
|
|
CalculateRowHeight();
|
|
|
UpdateRecordCount();
|
|
|
+
|
|
|
+ _invalidating = false;
|
|
|
}
|
|
|
|
|
|
private void ProcessRow(CoreRow innerRow, CoreRow row)
|
|
@@ -1180,12 +1208,15 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
public void InvalidateRow(CoreRow row)
|
|
|
{
|
|
|
if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return;
|
|
|
+ _invalidating = true;
|
|
|
|
|
|
var _innerRow = _innerTable.Rows[row.Index];
|
|
|
ProcessRow(_innerRow, row);
|
|
|
|
|
|
var coreTreeNode = Nodes.Find(_innerRow);
|
|
|
coreTreeNode?.InvalidateData();
|
|
|
+
|
|
|
+ _invalidating = false;
|
|
|
}
|
|
|
|
|
|
|
|
@@ -1194,16 +1225,246 @@ public class DynamicGridTreeUIComponent<T> : IDynamicGridUIComponent<T>, IDynami
|
|
|
_tree.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
|
|
|
}
|
|
|
|
|
|
+ private CoreTreeNode? GetNode(CoreRow row)
|
|
|
+ {
|
|
|
+ if (_innerTable is null || row.Index < 0 || row.Index >= _innerTable.Rows.Count) return null;
|
|
|
+
|
|
|
+ var _innerRow = _innerTable.Rows[row.Index];
|
|
|
+ var node = Nodes.Find(_innerRow);
|
|
|
+ return node;
|
|
|
+ }
|
|
|
+
|
|
|
public void UpdateCell(CoreRow row, string column, object? value)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ var node = GetNode(row);
|
|
|
+ if(node is not null)
|
|
|
+ {
|
|
|
+ node[column] = value;
|
|
|
+ node.InvalidateData();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
public void UpdateRow(CoreRow row)
|
|
|
{
|
|
|
- throw new NotImplementedException();
|
|
|
+ var dataRow = GetNode(row);
|
|
|
+ if(dataRow is not null)
|
|
|
+ {
|
|
|
+ foreach(var (key, value) in row)
|
|
|
+ {
|
|
|
+ dataRow[key] = value;
|
|
|
+ }
|
|
|
+ for (var i = 0; i < ActionColumns.Count; i++)
|
|
|
+ dataRow[$"_ActionColumn{i}"] = ActionColumns[i].Data(row);
|
|
|
+ dataRow.InvalidateData();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ #region Direct Edit
|
|
|
+
|
|
|
+ private void _tree_PreviewKeyUp(object sender, KeyEventArgs e)
|
|
|
+ {
|
|
|
+ if (e.Key == Key.OemPeriod)
|
|
|
+ {
|
|
|
+ if (e.OriginalSource is Syncfusion.Windows.Shared.TimeSpanEdit editor && editor.SelectionStart < 2)
|
|
|
+ {
|
|
|
+ editor.SelectionStart = 3;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (e.Key == Key.Tab)
|
|
|
+ {
|
|
|
+ if (Parent.IsDirectEditMode())
|
|
|
+ {
|
|
|
+ _tree.SelectionController.CurrentCellManager.EndEdit();
|
|
|
+ _tree.MoveFocus(new TraversalRequest(FocusNavigationDirection.Right));
|
|
|
+ _tree.SelectionController.CurrentCellManager.BeginEdit();
|
|
|
+ e.Handled = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool bChanged;
|
|
|
+
|
|
|
+ private class DirectEditingObject
|
|
|
+ {
|
|
|
+ public T Object { get; set; }
|
|
|
+
|
|
|
+ public CoreRow Row { get; set; }
|
|
|
+
|
|
|
+ public CoreTreeNode? Node { get; set; }
|
|
|
+
|
|
|
+ public DirectEditingObject(T obj, CoreRow row, CoreTreeNode? node)
|
|
|
+ {
|
|
|
+ Object = obj;
|
|
|
+ Row = row;
|
|
|
+ Node = node;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private DirectEditingObject? _editingObject;
|
|
|
+
|
|
|
+ private DirectEditingObject EnsureEditingObject(CoreRow row)
|
|
|
+ {
|
|
|
+ _editingObject ??= new(Parent.LoadItem(row), row, GetNode(row));
|
|
|
+ return _editingObject;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void UpdateData(string column, Dictionary<CoreColumn, object?> updates)
|
|
|
+ {
|
|
|
+ if (_editingObject is null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ var coreRow = _editingObject.Row;
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ Parent.UpdateData(_editingObject.Object, coreRow, column, updates);
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ MessageWindow.ShowError($"Error saving {typeof(T)}", e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private void UpdateData(CoreTreeNode node, int columnIndex)
|
|
|
+ {
|
|
|
+ if (GetColumn(columnIndex) is DynamicGridColumn gridcol)
|
|
|
+ {
|
|
|
+ var datacol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(gridcol.ColumnName));
|
|
|
+ if (datacol != null)
|
|
|
+ {
|
|
|
+ var value = node?[datacol.ColumnName];
|
|
|
+ if (value is null)
|
|
|
+ value = CoreUtils.GetDefault(datacol.DataType);
|
|
|
+ else
|
|
|
+ value = CoreUtils.ChangeType(value, datacol.DataType);
|
|
|
+
|
|
|
+ UpdateData(datacol.ColumnName, new Dictionary<CoreColumn, object?>() { { datacol, value } });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Dictionary<string, CoreTable> _lookups = new();
|
|
|
+ private void _tree_CurrentCellBeginEdit(object? sender, TreeGridCurrentCellBeginEditEventArgs e)
|
|
|
+ {
|
|
|
+ var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
|
|
|
+ if (row is null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ EnsureEditingObject(row);
|
|
|
+
|
|
|
+ if (_tree.Columns[e.RowColumnIndex.ColumnIndex] is TreeGridComboBoxColumn column && column.ItemsSource == null)
|
|
|
+ {
|
|
|
+ var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
|
|
|
+ if(gridColumn is DynamicGridColumn col)
|
|
|
+ {
|
|
|
+ var property = col.ColumnName;
|
|
|
+ var prop = CoreUtils.GetProperty(typeof(T), property);
|
|
|
+ var editor = prop.GetEditor();
|
|
|
+ if (editor is ILookupEditor lookupEditor)
|
|
|
+ {
|
|
|
+ if (!_lookups.ContainsKey(property))
|
|
|
+ _lookups[property] = lookupEditor.Values(typeof(T), property);
|
|
|
+ var combo = column;
|
|
|
+ combo.ItemsSource = _lookups[property].ToDictionary(_lookups[property].Columns[0].ColumnName, "Display");
|
|
|
+ combo.SelectedValuePath = "Key";
|
|
|
+ combo.DisplayMemberPath = "Value";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ bChanged = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Nodes_ColumnChanged(CoreTreeNode node, string column)
|
|
|
+ {
|
|
|
+ if (_invalidating) return;
|
|
|
+
|
|
|
+ var row = GetRow(node);
|
|
|
+ if (row is null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ var data = Parent.Data;
|
|
|
+
|
|
|
+ var dataCol = Parent.Data.Columns.FirstOrDefault(x => x.ColumnName.Equals(column));
|
|
|
+ var col = ColumnList.OfType<DynamicGridColumn>()
|
|
|
+ .FirstOrDefault(x => x.ColumnName.Equals(column));
|
|
|
+
|
|
|
+ if (col is null || dataCol is null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (col is DynamicGridCheckBoxColumn<T>)
|
|
|
+ {
|
|
|
+ EnsureEditingObject(row);
|
|
|
+ if(_editingObject is not null)
|
|
|
+ {
|
|
|
+ var value = node[column];
|
|
|
+
|
|
|
+ _invalidating = true;
|
|
|
+ UpdateData(column, new Dictionary<CoreColumn, object?>() { { dataCol, value } });
|
|
|
+ _invalidating = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ _editingObject = null;
|
|
|
+ }
|
|
|
+ if (_editingObject is not null)
|
|
|
+ bChanged = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void _tree_CurrentCellDropDownSelectionChanged(object? sender, CurrentCellDropDownSelectionChangedEventArgs e)
|
|
|
+ {
|
|
|
+ var row = GetRowFromIndex(e.RowColumnIndex.RowIndex);
|
|
|
+ if (row is null)
|
|
|
+ return;
|
|
|
+ EnsureEditingObject(row);
|
|
|
+ if ((_editingObject is not null) && (e.SelectedItem is Tuple<object?, string> tuple))
|
|
|
+ {
|
|
|
+ var gridColumn = GetColumn(e.RowColumnIndex.ColumnIndex);
|
|
|
+ if (gridColumn is DynamicGridColumn col)
|
|
|
+ {
|
|
|
+ var corecol = col.ColumnName;
|
|
|
+
|
|
|
+ var updates = new Dictionary<CoreColumn, object?>();
|
|
|
+
|
|
|
+ var prefix = string.Join(".", corecol.Split(".").Reverse().Skip(1).Reverse());
|
|
|
+ var field = corecol.Split(".").Last();
|
|
|
+
|
|
|
+ var prop = CoreUtils.GetProperty(typeof(T), corecol);
|
|
|
+ if (prop.GetEditor() is ILookupEditor editor)
|
|
|
+ {
|
|
|
+ var data = editor.Values(typeof(T), corecol);
|
|
|
+ var lookuprow = data.Rows.FirstOrDefault(r => Equals(r[field], tuple.Item1))
|
|
|
+ ?? data.NewRow(true);
|
|
|
+
|
|
|
+ foreach (CoreColumn lookupcol in data.Columns)
|
|
|
+ {
|
|
|
+ var columnname = String.IsNullOrWhiteSpace(prefix)
|
|
|
+ ? lookupcol.ColumnName
|
|
|
+ : String.Join(".", prefix, lookupcol.ColumnName);
|
|
|
+ var updatecol = Parent.Data.Columns.FirstOrDefault(x => String.Equals(x.ColumnName, columnname));
|
|
|
+ if (updatecol != null)
|
|
|
+ updates[updatecol] = lookuprow[lookupcol.ColumnName];
|
|
|
+ }
|
|
|
+ UpdateData(corecol, updates);
|
|
|
+ bChanged = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void _tree_CurrentCellEndEdit(object? sender, CurrentCellEndEditEventArgs e)
|
|
|
+ {
|
|
|
+ if (_editingObject is not null && bChanged)
|
|
|
+ {
|
|
|
+ UpdateData(_editingObject.Node, e.RowColumnIndex.ColumnIndex);
|
|
|
+ }
|
|
|
+ if (bChanged)
|
|
|
+ Parent.DoChanged();
|
|
|
+ bChanged = false;
|
|
|
+ _editingObject = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
#region Drag + Drop
|
|
|
|
|
|
private void _tree_DragOver(object sender, DragEventArgs e)
|