Skip to content

Commit 252ddd1

Browse files
committed
Allow controls to be deleted
Control can be deleted now if they don't have an existing reference in any of the project. Additioinal validations are now in place to prevent editing or using a control if that is marked deleted. This can happen if a client has marked control to be deleted and another client with stale data is trying to use or edit the control.
1 parent ad9885e commit 252ddd1

File tree

20 files changed

+343
-90
lines changed

20 files changed

+343
-90
lines changed

src/Pixel.Automation.AppExplorer.ViewModels/Application/ApplicationExplorerViewModel.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,13 @@ public async Task AddApplication(KnownApplication knownApplication)
206206

207207
public async Task EditApplicationAsync(ApplicationDescriptionViewModel applicationDescriptionViewModel)
208208
{
209-
await this.eventAggregator.PublishOnUIThreadAsync(new PropertyGridObjectEventArgs(applicationDescriptionViewModel.ApplicationDetails, () => { _ = SaveApplicationAsync(applicationDescriptionViewModel); }, () => { return true; }));
209+
await this.eventAggregator.PublishOnUIThreadAsync(new PropertyGridObjectEventArgs(applicationDescriptionViewModel.ApplicationDetails,
210+
async () => {
211+
await SaveApplicationAsync(applicationDescriptionViewModel);
212+
},
213+
() => {
214+
return true;
215+
}));
210216
}
211217

212218
public async Task SaveApplicationAsync(ApplicationDescriptionViewModel applicationDescriptionViewModel)

src/Pixel.Automation.AppExplorer.ViewModels/Control/ControlExplorerViewModel.cs

Lines changed: 110 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ public void ToggleRename(ControlDescriptionViewModel targetControl)
162162
/// <param name="controlToRename"></param>
163163
public async Task RenameControl(ActionExecutionContext context, ControlDescriptionViewModel controlToRename)
164164
{
165+
string currentName = controlToRename.ControlName;
165166
try
166167
{
167168
var keyArgs = context.EventArgs as KeyEventArgs;
@@ -180,7 +181,9 @@ public async Task RenameControl(ActionExecutionContext context, ControlDescripti
180181
catch (Exception ex)
181182
{
182183
logger.Error(ex, ex.Message);
184+
controlToRename.ControlName = currentName;
183185
CanEdit = false;
186+
MessageBox.Show(ex.Message, $"Error while renaming control : {controlToRename.ControlName}");
184187
}
185188
}
186189

@@ -242,7 +245,11 @@ private IEnumerable<ControlDescriptionViewModel> LoadControlDetails(ApplicationD
242245
var controls = this.applicationDataManager.GetControlsForScreen(applicationDescriptionViewModel.Model, screenName).ToList();
243246
foreach (var control in controls)
244247
{
245-
var controlDescriptionViewModel = new ControlDescriptionViewModel(control);
248+
if(control.IsDeleted)
249+
{
250+
continue;
251+
}
252+
var controlDescriptionViewModel = new ControlDescriptionViewModel(control);
246253
applicationDescriptionViewModel.AddControl(controlDescriptionViewModel, screenName);
247254
controlsList.Add(controlDescriptionViewModel);
248255
}
@@ -276,21 +283,29 @@ public async Task MoveToScreen(ControlDescriptionViewModel controlDescription)
276283
/// <returns></returns>
277284
public async Task ConfigureControlAsync(ControlDescriptionViewModel controlToEdit)
278285
{
279-
Guard.Argument(controlToEdit).NotNull();
280-
281-
//Make a copy of ControlDescription that is opened for edit
282-
var copyOfControlToEdit = controlToEdit.ControlDescription.Clone() as ControlDescription;
283-
copyOfControlToEdit.ControlId = controlToEdit.ControlId;
284-
var controlEditor = controlEditorFactory.CreateControlEditor(controlToEdit.ControlDetails);
285-
controlEditor.Initialize(copyOfControlToEdit);
286-
var result = await windowManager.ShowDialogAsync(controlEditor);
287-
//if save was clicked, assign back changes in ControlDetails to controlToEdit.
288-
//Editor only allows editing ControlDetails. Description won't be modified.
289-
if (result.HasValue && result.Value)
286+
try
290287
{
291-
controlToEdit.ControlDetails = copyOfControlToEdit.ControlDetails;
292-
await SaveControlDetails(controlToEdit, false);
293-
await this.eventAggregator.PublishOnBackgroundThreadAsync(new ControlUpdatedEventArgs(controlToEdit.ControlId));
288+
Guard.Argument(controlToEdit).NotNull();
289+
290+
//Make a copy of ControlDescription that is opened for edit
291+
var copyOfControlToEdit = controlToEdit.ControlDescription.Clone() as ControlDescription;
292+
copyOfControlToEdit.ControlId = controlToEdit.ControlId;
293+
var controlEditor = controlEditorFactory.CreateControlEditor(controlToEdit.ControlDetails);
294+
controlEditor.Initialize(copyOfControlToEdit);
295+
var result = await windowManager.ShowDialogAsync(controlEditor);
296+
//if save was clicked, assign back changes in ControlDetails to controlToEdit.
297+
//Editor only allows editing ControlDetails. Description won't be modified.
298+
if (result.HasValue && result.Value)
299+
{
300+
controlToEdit.ControlDetails = copyOfControlToEdit.ControlDetails;
301+
await SaveControlDetails(controlToEdit, false);
302+
await this.eventAggregator.PublishOnBackgroundThreadAsync(new ControlUpdatedEventArgs(controlToEdit.ControlId));
303+
}
304+
}
305+
catch (Exception ex)
306+
{
307+
logger.Error(ex, ex.Message);
308+
MessageBox.Show(ex.Message, $"Error while configuring control : {controlToEdit.ControlName}");
294309
}
295310
}
296311

@@ -301,9 +316,43 @@ public async Task ConfigureControlAsync(ControlDescriptionViewModel controlToEdi
301316
/// <returns></returns>
302317
public async Task EditControlAsync(ControlDescriptionViewModel controlToEdit)
303318
{
304-
await this.eventAggregator.PublishOnUIThreadAsync(new PropertyGridObjectEventArgs(controlToEdit, () => { _ = SaveControlDetails(controlToEdit, false); }, () => { return true; }));
319+
await this.eventAggregator.PublishOnUIThreadAsync(new PropertyGridObjectEventArgs(controlToEdit,
320+
async () => {
321+
try
322+
{
323+
await SaveControlDetails(controlToEdit, false);
324+
}
325+
catch(Exception ex)
326+
{
327+
logger.Error(ex, "There was an error while trying to edit control : {0}", controlToEdit.ControlName);
328+
MessageBox.Show(ex.Message, $"Error while editing control : {controlToEdit.ControlName}");
329+
}
330+
},
331+
() => {
332+
return true;
333+
}));
305334
}
306335

336+
/// <summary>
337+
/// Delete the control
338+
/// </summary>
339+
/// <param name="controlToDelete"></param>
340+
public async Task DeleteControlAsync(ControlDescriptionViewModel controlToDelete)
341+
{
342+
try
343+
{
344+
Guard.Argument(controlToDelete, nameof(controlToDelete)).NotNull();
345+
await this.applicationDataManager.DeleteControlAsync(controlToDelete.ControlDescription);
346+
this.Controls.Remove(controlToDelete);
347+
}
348+
catch (Exception ex)
349+
{
350+
logger.Error(ex, "There was an error while trying to delete control : {0}", controlToDelete.ControlName);
351+
MessageBox.Show(ex.Message, $"Error while deleting control : {controlToDelete.ControlName}");
352+
}
353+
}
354+
355+
307356
/// <summary>
308357
/// Show file browse dialog and let user pick a new image for the control.
309358
/// Existing image will be deleted and replaced with the new image picked by user.
@@ -312,25 +361,33 @@ public async Task EditControlAsync(ControlDescriptionViewModel controlToEdit)
312361
/// <returns></returns>
313362
public async Task ChangeImageFromExistingAsync(ControlDescriptionViewModel selectedControl)
314363
{
315-
OpenFileDialog openFileDialog = new OpenFileDialog();
316-
openFileDialog.Filter = "PNG File (*.Png)|*.Png";
317-
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
318-
if (openFileDialog.ShowDialog() == true)
364+
try
319365
{
320-
string fileName = openFileDialog.FileName;
321-
File.Delete(selectedControl.ControlImage);
322-
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
366+
OpenFileDialog openFileDialog = new OpenFileDialog();
367+
openFileDialog.Filter = "PNG File (*.Png)|*.Png";
368+
openFileDialog.InitialDirectory = Environment.CurrentDirectory;
369+
if (openFileDialog.ShowDialog() == true)
323370
{
324-
//we can't reuse the same control image name due to caching issues with bitmap which will keep using old file
325-
//unless file name changes
326-
selectedControl.ControlImage = await this.applicationDataManager.AddOrUpdateControlImageAsync(selectedControl.ControlDescription, fs);
327-
await SaveControlDetails(selectedControl, false);
328-
// This will force reload image on control explorer
329-
selectedControl.ImageSource = null;
330-
// This will force reload image on process designer
331-
await this.eventAggregator.PublishOnBackgroundThreadAsync(new ControlUpdatedEventArgs(selectedControl.ControlId));
371+
string fileName = openFileDialog.FileName;
372+
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
373+
{
374+
//we can't reuse the same control image name due to caching issues with bitmap which will keep using old file
375+
//unless file name changes
376+
selectedControl.ControlImage = await this.applicationDataManager.AddOrUpdateControlImageAsync(selectedControl.ControlDescription, fs);
377+
await SaveControlDetails(selectedControl, false);
378+
// This will force reload image on control explorer
379+
selectedControl.ImageSource = null;
380+
// This will force reload image on process designer
381+
await this.eventAggregator.PublishOnBackgroundThreadAsync(new ControlUpdatedEventArgs(selectedControl.ControlId));
382+
}
383+
File.Delete(selectedControl.ControlImage);
332384
}
333385
}
386+
catch (Exception ex)
387+
{
388+
logger.Error(ex, "There was an error while trying to change image for control : {0}", selectedControl.ControlName);
389+
MessageBox.Show(ex.Message, $"Error while changing image for control : {selectedControl.ControlName}");
390+
}
334391
}
335392

336393
/// <summary>
@@ -353,7 +410,8 @@ public async Task CloneControl(ControlDescriptionViewModel controlToClone)
353410
}
354411
catch (Exception ex)
355412
{
356-
logger.Error(ex, "There was an error while trying to clone the control.");
413+
logger.Error(ex, "There was an error while trying to clone control : {0}", selectedControl.ControlName);
414+
MessageBox.Show(ex.Message, $"Error while cloning control : {selectedControl.ControlName}");
357415
}
358416
}
359417

@@ -363,18 +421,26 @@ public async Task CloneControl(ControlDescriptionViewModel controlToClone)
363421
/// <param name="controlToRename"></param>
364422
public async Task CreateRevision(ControlDescriptionViewModel control)
365423
{
366-
Guard.Argument(control).NotNull();
367-
368-
var clonedControl = control.ControlDescription.Clone() as ControlDescription;
369-
clonedControl.ControlId = control.ControlId;
370-
clonedControl.Version = new Version(control.Version.Major + 1, 0);
371-
var controlDescriptionViewModel = new ControlDescriptionViewModel(clonedControl);
372-
await SaveBitMapSource(controlDescriptionViewModel.ControlDescription, controlDescriptionViewModel.ImageSource);
373-
await SaveControlDetails(controlDescriptionViewModel, true);
374-
//we remove the visible version and add the new revised version in explorer.
375-
this.Controls.Remove(control);
376-
this.Controls.Add(controlDescriptionViewModel);
377-
logger.Information("Created a new revision for control : {0}", control.ControlDescription);
424+
try
425+
{
426+
Guard.Argument(control).NotNull();
427+
428+
var clonedControl = control.ControlDescription.Clone() as ControlDescription;
429+
clonedControl.ControlId = control.ControlId;
430+
clonedControl.Version = new Version(control.Version.Major + 1, 0);
431+
var controlDescriptionViewModel = new ControlDescriptionViewModel(clonedControl);
432+
await SaveBitMapSource(controlDescriptionViewModel.ControlDescription, controlDescriptionViewModel.ImageSource);
433+
await SaveControlDetails(controlDescriptionViewModel, true);
434+
//we remove the visible version and add the new revised version in explorer.
435+
this.Controls.Remove(control);
436+
this.Controls.Add(controlDescriptionViewModel);
437+
logger.Information("Created a new revision for control : {0}", control.ControlDescription);
438+
}
439+
catch (Exception ex)
440+
{
441+
logger.Error(ex, "There was an error while creating revision of control : {0}", selectedControl.ControlName);
442+
MessageBox.Show(ex.Message, $"Error while creating revision for control : {selectedControl.ControlName}");
443+
}
378444
}
379445

380446
private readonly object locker = new object();

src/Pixel.Automation.AppExplorer.Views/Control/ControlExplorerView.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
<MenuItem x:Name="CreateRevision" Header="Create Revision" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action CreateRevision($dataContext)]"></MenuItem>
7777
<MenuItem x:Name="ChangeImage" Header="Change Image" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action ChangeImageFromExistingAsync($dataContext)]"></MenuItem>
7878
<MenuItem x:Name="MoveToScreen" Header="Move To Screen" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action MoveToScreen($dataContext)]"></MenuItem>
79+
<MenuItem x:Name="Delete" Header="Delete" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action DeleteControlAsync($dataContext)]" ></MenuItem>
7980
</StackPanel>
8081
</ControlTemplate>
8182
</ContextMenu.Template>

src/Pixel.Automation.Core/Controls/ControlDescription.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ public class ControlDescription : ICloneable
5454
[DataMember(Order = 100)]
5555
public IControlIdentity ControlDetails { get; set; }
5656

57+
/// <summary>
58+
/// Indicates if the Control was deleted.
59+
/// </summary>
60+
[DataMember(Order = 1000)]
61+
public bool IsDeleted { get; set; }
62+
5763
/// <summary>
5864
/// Default constructor
5965
/// </summary>

src/Pixel.Automation.Designer.ViewModels/AutomationBuilder/EditorViewModel.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,17 @@ public async Task HandleAsync(TestEntityRemovedEventArgs removedEntity, Cancella
411411
/// <returns></returns>
412412
public async Task HandleAsync(ControlAddedEventArgs control, CancellationToken cancellationToken)
413413
{
414-
var controlDescription = control.Control;
415-
var referenceManager = this.projectManager.GetReferenceManager();
416-
await referenceManager.AddControlReferenceAsync(new ControlReference(controlDescription.ApplicationId, controlDescription.ControlId, controlDescription.Version));
414+
var controlDescription = control.Control;
415+
try
416+
{
417+
var referenceManager = this.projectManager.GetReferenceManager();
418+
await referenceManager.AddControlReferenceAsync(new ControlReference(controlDescription.ApplicationId, controlDescription.ControlId, controlDescription.Version));
419+
}
420+
catch (Exception ex)
421+
{
422+
logger.Error(ex, "There was an error while trying to add control reference for control : {0}", controlDescription.ControlName);
423+
MessageBox.Show(ex.Message, $"Error while adding control : {controlDescription.ControlName}");
424+
}
417425
}
418426

419427

src/Pixel.Automation.Designer.ViewModels/PropertyGrid/PropertyGridViewModel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ public PropertyGridViewModel()
3333
this.SaveCommand = new RelayCommand(p => Save(), p => CanSave());
3434
}
3535

36-
public void SetState(object selectedObject, bool isReadOnly, Action saveCommand, Func<bool> canSave)
36+
public void SetState(object selectedObject, bool isReadOnly, Func<Task> saveCommand, Func<bool> canSave)
3737
{
3838
this.selectedObject = selectedObject;
3939
this.isReadOnly = isReadOnly;
40-
this.onSave = saveCommand;
40+
this.onSave = () => saveCommand();
4141
this.canSave = canSave;
4242
NotifyOfPropertyChange(() => SelectedObject);
4343
NotifyOfPropertyChange(() => IsReadOnly);

src/Pixel.Automation.Editor.Notifications/EventArgs/PropertyGridObjectEventArgs.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ public class PropertyGridObjectEventArgs : EventArgs
66

77
public bool IsReadOnly { get; private set; }
88

9-
public Action SaveCommand { get; private set; }
9+
public Func<Task> SaveCommand { get; private set; }
1010

1111
public Func<bool> CanSaveCommand { get; private set; }
1212

@@ -21,7 +21,7 @@ public PropertyGridObjectEventArgs(Object objectToDisplay, bool isReadOnly) : ba
2121
this.IsReadOnly = isReadOnly;
2222
}
2323

24-
public PropertyGridObjectEventArgs(Object objectToDisplay, Action saveCommand, Func<bool> canSaveCommand) : this(objectToDisplay, false)
24+
public PropertyGridObjectEventArgs(Object objectToDisplay, Func<Task> saveCommand, Func<bool> canSaveCommand) : this(objectToDisplay, false)
2525
{
2626
this.SaveCommand = saveCommand;
2727
this.CanSaveCommand = canSaveCommand;

src/Pixel.Automation.Reference.Manager/ReferenceManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,11 @@ public async Task AddControlReferenceAsync(ControlReference controlReference)
127127
Guard.Argument(controlReference, nameof(controlReference)).NotNull();
128128
if (!this.controlReferences.HasReference(controlReference))
129129
{
130-
this.controlReferences.AddControlReference(controlReference);
131130
if (IsOnlineMode)
132131
{
133132
await this.referencesRepositoryClient.AddOrUpdateControlReferences(this.projectId, this.projectVersion, controlReference);
134133
}
134+
this.controlReferences.AddControlReference(controlReference);
135135
SaveLocal();
136136
}
137137
}

0 commit comments

Comments
 (0)