|
@@ -323,6 +323,7 @@ namespace InABox.DynamicGrid
|
|
|
private readonly Label EditSpacer;
|
|
|
private readonly Button Export;
|
|
|
private readonly Label ExportSpacer;
|
|
|
+ private readonly Button DuplicateBtn;
|
|
|
|
|
|
private readonly GridRowSizingOptions gridRowResizingOptions = new() { CanIncludeHiddenColumns = false, AutoFitMode = AutoFitMode.SmartFit };
|
|
|
private readonly Button Help;
|
|
@@ -382,6 +383,11 @@ namespace InABox.DynamicGrid
|
|
|
MasterColumns = new DynamicGridColumns();
|
|
|
MasterColumns.ExtractColumns(typeof(T), "");
|
|
|
|
|
|
+ foreach(var column in LookupFactory.RequiredColumns<T>().ColumnNames())
|
|
|
+ {
|
|
|
+ AddHiddenColumn(column);
|
|
|
+ }
|
|
|
+
|
|
|
ActionColumns = new DynamicActionColumns();
|
|
|
|
|
|
if (IsSequenced)
|
|
@@ -548,6 +554,8 @@ namespace InABox.DynamicGrid
|
|
|
Delete.SetValue(DockPanel.DockProperty, Dock.Right);
|
|
|
Delete.Click += Delete_Click;
|
|
|
|
|
|
+ DuplicateBtn = AddButton("Duplicate", Properties.Resources.paste.AsBitmapImage(Color.White), DoDuplicate);
|
|
|
+
|
|
|
Count = new Label();
|
|
|
Count.Height = 30;
|
|
|
Count.Margin = new Thickness(0, 2, 0, 0);
|
|
@@ -662,6 +670,9 @@ namespace InABox.DynamicGrid
|
|
|
up.Position = Options.Contains(DynamicGridOption.EditRows) ? DynamicActionColumnPosition.Start : DynamicActionColumnPosition.Hidden;
|
|
|
if (down != null)
|
|
|
down.Position = Options.Contains(DynamicGridOption.EditRows) ? DynamicActionColumnPosition.Start : DynamicActionColumnPosition.Hidden;
|
|
|
+
|
|
|
+ if (DuplicateBtn != null)
|
|
|
+ DuplicateBtn.Visibility = Visibility.Collapsed;
|
|
|
}
|
|
|
|
|
|
protected override DynamicGridStyleSelector<T> GetStyleSelector()
|
|
@@ -1028,6 +1039,9 @@ namespace InABox.DynamicGrid
|
|
|
protected virtual void SelectItems(CoreRow[]? rows)
|
|
|
{
|
|
|
OnSelectItem?.Invoke(this, new DynamicGridSelectionEventArgs(rows));
|
|
|
+
|
|
|
+ DuplicateBtn.Visibility =
|
|
|
+ typeof(T).IsAssignableTo(typeof(IDuplicatable)) && rows != null && rows.Length >= 1 ? Visibility.Visible : Visibility.Collapsed;
|
|
|
}
|
|
|
|
|
|
private bool bFilterVisible;
|
|
@@ -2092,6 +2106,8 @@ namespace InABox.DynamicGrid
|
|
|
|
|
|
#region Item Manipulation
|
|
|
|
|
|
+ #region Load/Save/Delete
|
|
|
+
|
|
|
protected virtual T[] LoadItems(CoreRow[] rows)
|
|
|
{
|
|
|
var result = new List<T>();
|
|
@@ -2143,6 +2159,10 @@ namespace InABox.DynamicGrid
|
|
|
DoDelete();
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Edit
|
|
|
+
|
|
|
protected virtual void DoEdit()
|
|
|
{
|
|
|
if (!SelectedRows.Any())
|
|
@@ -2270,11 +2290,6 @@ namespace InABox.DynamicGrid
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- protected virtual void ShowHelp(string slug)
|
|
|
- {
|
|
|
- Process.Start(new ProcessStartInfo("https://prs-software.com.au/wiki/index.php/" + slug) { UseShellExecute = true });
|
|
|
- }
|
|
|
-
|
|
|
protected virtual void DoAdd()
|
|
|
{
|
|
|
//CoreRow row = (SelectedRow > -1) && (SelectedRow < Data.Rows.Count) ? Data.Rows[this.SelectedRow] : null;
|
|
@@ -2289,138 +2304,6 @@ namespace InABox.DynamicGrid
|
|
|
{
|
|
|
DoAdd();
|
|
|
}
|
|
|
-
|
|
|
- protected void ReloadForms<TTargetType, TTargetForm, TSourceForm>(DynamicEditorForm editor, TTargetType item,
|
|
|
- Expression<Func<TSourceForm, object?>> sourcekey, Guid sourceid)
|
|
|
- where TTargetType : Entity, new()
|
|
|
- where TTargetForm : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
|
|
|
- where TSourceForm : Entity, IRemotable, IPersistent, IDigitalForm<TTargetType>, new()
|
|
|
- {
|
|
|
- var type = typeof(IDynamicOneToManyGrid<,>).MakeGenericType(typeof(TTargetType), typeof(TTargetForm));
|
|
|
- var page =
|
|
|
- editor.Pages?.FirstOrDefault(x => x.GetType().GetInterfaces().Contains(type)) as IDynamicOneToManyGrid<TTargetType, TTargetForm>;
|
|
|
- if (page != null && item != null)
|
|
|
- {
|
|
|
- if (!page.Ready)
|
|
|
- page.Load(item, null);
|
|
|
-
|
|
|
- CoreTable table;
|
|
|
- if (sourceid == Guid.Empty)
|
|
|
- {
|
|
|
- table = new CoreTable();
|
|
|
- table.LoadColumns(typeof(TSourceForm));
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- table = new Client<TSourceForm>().Query(
|
|
|
- new Filter<TSourceForm>(sourcekey).IsEqualTo(sourceid).And(x => x.Form.AppliesTo)
|
|
|
- .IsEqualTo(typeof(TTargetType).EntityName().Split('.').Last())
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- var newforms = new List<TTargetForm>();
|
|
|
- foreach (var row in table.Rows)
|
|
|
- {
|
|
|
- var sourceform = row.ToObject<TSourceForm>();
|
|
|
- var targetform = new TTargetForm();
|
|
|
-
|
|
|
- targetform.Form.ID = sourceform.Form.ID;
|
|
|
- targetform.Form.Synchronise(sourceform.Form);
|
|
|
- newforms.Add(targetform);
|
|
|
- }
|
|
|
-
|
|
|
- page.Items.Clear();
|
|
|
- page.LoadItems(newforms.ToArray());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- #region ClipBuffer
|
|
|
-
|
|
|
- private Tuple<ClipAction, CoreRow[]>? ClipBuffer;
|
|
|
-
|
|
|
- protected void ResetClipBuffer()
|
|
|
- {
|
|
|
- ClipBuffer = null;
|
|
|
- }
|
|
|
-
|
|
|
- protected void SetClipBuffer(ClipAction action, CoreRow[] rows)
|
|
|
- {
|
|
|
- ClipBuffer = new Tuple<ClipAction, CoreRow[]>(action, rows);
|
|
|
- }
|
|
|
-
|
|
|
- private void CutToClipBuffer()
|
|
|
- {
|
|
|
- SetClipBuffer(ClipAction.Cut, SelectedRows);
|
|
|
- InvalidateGrid();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private void CopyToClipBuffer()
|
|
|
- {
|
|
|
- SetClipBuffer(ClipAction.Copy, SelectedRows);
|
|
|
- InvalidateGrid();
|
|
|
- }
|
|
|
-
|
|
|
- private void PasteFromClipBuffer()
|
|
|
- {
|
|
|
- if (ClipBuffer == null)
|
|
|
- return;
|
|
|
-
|
|
|
- if (!IsSequenced)
|
|
|
- return;
|
|
|
-
|
|
|
- using (new WaitCursor())
|
|
|
- {
|
|
|
- var updates = ClipBuffer.Item2.Select(x => x.ToObject<T>()).ToList();
|
|
|
- if (BeforePaste(updates, ClipBuffer.Item1))
|
|
|
- {
|
|
|
- var currow = SelectedRows.FirstOrDefault()
|
|
|
- ?? Data.Rows.LastOrDefault();
|
|
|
-
|
|
|
- var sequence = currow != null ? currow.Get<T, long>(c => ((ISequenceable)c).Sequence) : 0;
|
|
|
-
|
|
|
- var postrows = Data.Rows.Where(r => !ClipBuffer.Item2.Contains(r) && r.Get<ISequenceable, long>(x => x.Sequence) >= sequence);
|
|
|
- updates.AddRange(LoadItems(postrows.ToArray()));
|
|
|
-
|
|
|
- foreach (var update in updates)
|
|
|
- {
|
|
|
- sequence++;
|
|
|
- ((ISequenceable)update).Sequence = sequence;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (updates.Any())
|
|
|
- {
|
|
|
- SaveItems(updates.ToArray());
|
|
|
- Refresh(false, true);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- protected virtual bool BeforePaste(IEnumerable<T> items, ClipAction action)
|
|
|
- {
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- #endregion
|
|
|
-
|
|
|
- private void Cut_Click(object sender, RoutedEventArgs e)
|
|
|
- {
|
|
|
- CutToClipBuffer();
|
|
|
- }
|
|
|
-
|
|
|
- private void Copy_Click(object sender, RoutedEventArgs e)
|
|
|
- {
|
|
|
- CopyToClipBuffer();
|
|
|
- }
|
|
|
-
|
|
|
- private void Paste_Click(object sender, RoutedEventArgs e)
|
|
|
- {
|
|
|
- PasteFromClipBuffer();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
public virtual DynamicEditorPages LoadEditorPages(T item)
|
|
|
{
|
|
|
DynamicEditorPages pages = new DynamicEditorPages();
|
|
@@ -2442,7 +2325,7 @@ namespace InABox.DynamicGrid
|
|
|
(f, i) =>
|
|
|
{
|
|
|
Process.Start(new ProcessStartInfo("https://prs-software.com.au/wiki/index.php/" + typeof(T).Name.SplitCamelCase().Replace(" ", "_"))
|
|
|
- { UseShellExecute = true });
|
|
|
+ { UseShellExecute = true });
|
|
|
}
|
|
|
);
|
|
|
}
|
|
@@ -2456,14 +2339,14 @@ namespace InABox.DynamicGrid
|
|
|
var cursor = new WaitCursor();
|
|
|
|
|
|
var pages = items.Length == 1 ? LoadEditorPages(items.First()) : new DynamicEditorPages();
|
|
|
-
|
|
|
+
|
|
|
var buttons = new DynamicEditorButtons();
|
|
|
if (items.Length == 1)
|
|
|
LoadEditorButtons(items.First(), buttons);
|
|
|
-
|
|
|
+
|
|
|
var editor = new DynamicEditorForm(items.Any() ? items.First().GetType() : typeof(T), pages, buttons, PageDataHandler, PreloadPages);
|
|
|
editor.SetValue(Panel.ZIndexProperty, 999);
|
|
|
-
|
|
|
+
|
|
|
editor.OnCustomiseColumns += (o, c) =>
|
|
|
{
|
|
|
ConfigureColumns(MasterColumns /*, true */);
|
|
@@ -2533,7 +2416,7 @@ namespace InABox.DynamicGrid
|
|
|
|
|
|
return editor.ShowDialog() == true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
private void UpdateEditor(DynamicEditorGrid grid, Expression<Func<IDimensioned, object?>> property, bool enabled)
|
|
|
{
|
|
|
if (!grid.TryFindEditor(new Column<IDimensioned>(property).Property, out var editor))
|
|
@@ -2552,15 +2435,14 @@ namespace InABox.DynamicGrid
|
|
|
{
|
|
|
if (items.First() is IDimensioned dimensioned)
|
|
|
{
|
|
|
- UpdateEditor(grid,x=>x.Dimensions.Quantity, dimensioned.Dimensions.GetUnit().HasQuantity);
|
|
|
- UpdateEditor(grid,x=>x.Dimensions.Length, dimensioned.Dimensions.GetUnit().HasLength);
|
|
|
- UpdateEditor(grid,x=>x.Dimensions.Width, dimensioned.Dimensions.GetUnit().HasWidth);
|
|
|
- UpdateEditor(grid,x=>x.Dimensions.Height, dimensioned.Dimensions.GetUnit().HasHeight);
|
|
|
- UpdateEditor(grid,x=>x.Dimensions.Weight, dimensioned.Dimensions.GetUnit().HasWeight);
|
|
|
+ UpdateEditor(grid, x => x.Dimensions.Quantity, dimensioned.Dimensions.GetUnit().HasQuantity);
|
|
|
+ UpdateEditor(grid, x => x.Dimensions.Length, dimensioned.Dimensions.GetUnit().HasLength);
|
|
|
+ UpdateEditor(grid, x => x.Dimensions.Width, dimensioned.Dimensions.GetUnit().HasWidth);
|
|
|
+ UpdateEditor(grid, x => x.Dimensions.Height, dimensioned.Dimensions.GetUnit().HasHeight);
|
|
|
+ UpdateEditor(grid, x => x.Dimensions.Weight, dimensioned.Dimensions.GetUnit().HasWeight);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private string[]? ValidateData(T[] items)
|
|
|
{
|
|
|
var errors = new List<string>();
|
|
@@ -2572,7 +2454,7 @@ namespace InABox.DynamicGrid
|
|
|
protected virtual void DoValidate(T[] items, List<string> errors)
|
|
|
{
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
|
|
|
|
|
|
protected virtual void AfterLoad(DynamicEditorForm editor, T[] items)
|
|
@@ -2592,7 +2474,7 @@ namespace InABox.DynamicGrid
|
|
|
foreach (var key in newchanges.Keys)
|
|
|
result[key] = newchanges[key];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return result;
|
|
|
}
|
|
|
|
|
@@ -2687,7 +2569,6 @@ namespace InABox.DynamicGrid
|
|
|
{
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private bool AddEditClick(CoreRow[]? rows)
|
|
|
{
|
|
|
if (!IsEnabled || bRefreshing)
|
|
@@ -2747,11 +2628,184 @@ namespace InABox.DynamicGrid
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Duplicate
|
|
|
+
|
|
|
+ protected virtual IEnumerable<T> LoadDuplicatorItems(CoreRow[] rows)
|
|
|
+ {
|
|
|
+ return LoadItems(rows);
|
|
|
+ }
|
|
|
+
|
|
|
+ private bool DoDuplicate(Button button, CoreRow[] rows)
|
|
|
+ {
|
|
|
+ if (!rows.Any())
|
|
|
+ {
|
|
|
+ MessageBox.Show("Please select at least one record to duplicate!");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*var ids = ExtractValues(x => x.ID, Selection.Selected).ToArray();
|
|
|
+ if (!ids.Any())
|
|
|
+ {
|
|
|
+ MessageBox.Show("Please select at least one record to duplicate!");
|
|
|
+ return false;
|
|
|
+ }*/
|
|
|
+
|
|
|
+ var duplicator = (new T() as IDuplicatable)?.GetDuplicator();
|
|
|
+ if (duplicator is null)
|
|
|
+ {
|
|
|
+ MessageBox.Show($"Cannot duplicate {typeof(T)}");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ duplicator.Duplicate(LoadDuplicatorItems(rows));// new Filter<T>(x => x.ID).InList(ids));
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ protected virtual void ShowHelp(string slug)
|
|
|
+ {
|
|
|
+ Process.Start(new ProcessStartInfo("https://prs-software.com.au/wiki/index.php/" + slug) { UseShellExecute = true });
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void ReloadForms<TTargetType, TTargetForm, TSourceForm>(DynamicEditorForm editor, TTargetType item,
|
|
|
+ Expression<Func<TSourceForm, object?>> sourcekey, Guid sourceid)
|
|
|
+ where TTargetType : Entity, new()
|
|
|
+ where TTargetForm : Entity, IRemotable, IPersistent, IDigitalFormInstance, new()
|
|
|
+ where TSourceForm : Entity, IRemotable, IPersistent, IDigitalForm<TTargetType>, new()
|
|
|
+ {
|
|
|
+ var type = typeof(IDynamicOneToManyGrid<,>).MakeGenericType(typeof(TTargetType), typeof(TTargetForm));
|
|
|
+ var page =
|
|
|
+ editor.Pages?.FirstOrDefault(x => x.GetType().GetInterfaces().Contains(type)) as IDynamicOneToManyGrid<TTargetType, TTargetForm>;
|
|
|
+ if (page != null && item != null)
|
|
|
+ {
|
|
|
+ if (!page.Ready)
|
|
|
+ page.Load(item, null);
|
|
|
+
|
|
|
+ CoreTable table;
|
|
|
+ if (sourceid == Guid.Empty)
|
|
|
+ {
|
|
|
+ table = new CoreTable();
|
|
|
+ table.LoadColumns(typeof(TSourceForm));
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ table = new Client<TSourceForm>().Query(
|
|
|
+ new Filter<TSourceForm>(sourcekey).IsEqualTo(sourceid).And(x => x.Form.AppliesTo)
|
|
|
+ .IsEqualTo(typeof(TTargetType).EntityName().Split('.').Last())
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ var newforms = new List<TTargetForm>();
|
|
|
+ foreach (var row in table.Rows)
|
|
|
+ {
|
|
|
+ var sourceform = row.ToObject<TSourceForm>();
|
|
|
+ var targetform = new TTargetForm();
|
|
|
+
|
|
|
+ targetform.Form.ID = sourceform.Form.ID;
|
|
|
+ targetform.Form.Synchronise(sourceform.Form);
|
|
|
+ newforms.Add(targetform);
|
|
|
+ }
|
|
|
+
|
|
|
+ page.Items.Clear();
|
|
|
+ page.LoadItems(newforms.ToArray());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ #region ClipBuffer
|
|
|
+
|
|
|
+ private Tuple<ClipAction, CoreRow[]>? ClipBuffer;
|
|
|
+
|
|
|
+ protected void ResetClipBuffer()
|
|
|
+ {
|
|
|
+ ClipBuffer = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ protected void SetClipBuffer(ClipAction action, CoreRow[] rows)
|
|
|
+ {
|
|
|
+ ClipBuffer = new Tuple<ClipAction, CoreRow[]>(action, rows);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void CutToClipBuffer()
|
|
|
+ {
|
|
|
+ SetClipBuffer(ClipAction.Cut, SelectedRows);
|
|
|
+ InvalidateGrid();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private void CopyToClipBuffer()
|
|
|
+ {
|
|
|
+ SetClipBuffer(ClipAction.Copy, SelectedRows);
|
|
|
+ InvalidateGrid();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void PasteFromClipBuffer()
|
|
|
+ {
|
|
|
+ if (ClipBuffer == null)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (!IsSequenced)
|
|
|
+ return;
|
|
|
+
|
|
|
+ using (new WaitCursor())
|
|
|
+ {
|
|
|
+ var updates = ClipBuffer.Item2.Select(x => x.ToObject<T>()).ToList();
|
|
|
+ if (BeforePaste(updates, ClipBuffer.Item1))
|
|
|
+ {
|
|
|
+ var currow = SelectedRows.FirstOrDefault()
|
|
|
+ ?? Data.Rows.LastOrDefault();
|
|
|
+
|
|
|
+ var sequence = currow != null ? currow.Get<T, long>(c => ((ISequenceable)c).Sequence) : 0;
|
|
|
+
|
|
|
+ var postrows = Data.Rows.Where(r => !ClipBuffer.Item2.Contains(r) && r.Get<ISequenceable, long>(x => x.Sequence) >= sequence);
|
|
|
+ updates.AddRange(LoadItems(postrows.ToArray()));
|
|
|
+
|
|
|
+ foreach (var update in updates)
|
|
|
+ {
|
|
|
+ sequence++;
|
|
|
+ ((ISequenceable)update).Sequence = sequence;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (updates.Any())
|
|
|
+ {
|
|
|
+ SaveItems(updates.ToArray());
|
|
|
+ Refresh(false, true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ protected virtual bool BeforePaste(IEnumerable<T> items, ClipAction action)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ private void Cut_Click(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ CutToClipBuffer();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Copy_Click(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ CopyToClipBuffer();
|
|
|
+ }
|
|
|
+
|
|
|
+ private void Paste_Click(object sender, RoutedEventArgs e)
|
|
|
+ {
|
|
|
+ PasteFromClipBuffer();
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
protected virtual void ObjectToRow(T obj, CoreRow row)
|
|
|
{
|
|
|
Data.LoadRow(row, obj);
|
|
|
}
|
|
|
|
|
|
+ #region Import / Export
|
|
|
+
|
|
|
protected virtual Guid GetImportID()
|
|
|
{
|
|
|
return Guid.Empty;
|
|
@@ -2859,6 +2913,8 @@ namespace InABox.DynamicGrid
|
|
|
ExcelExporter.DoExport(data, filename);
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
public void ScrollIntoView(CoreRow row)
|
|
|
{
|
|
|
DataGrid.ScrollInView(new RowColumnIndex(row.Index + 1, 0));
|