Skip to content

Commit 95d4bb4

Browse files
authored
Fixed Select and SelectArea to clear the existing selection and select the containers within the same transaction (#172)
1 parent 0c8a4f9 commit 95d4bb4

File tree

8 files changed

+114
-99
lines changed

8 files changed

+114
-99
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
> - Features:
1717
> - Added BeginPanning, UpdatePanning, EndPanning, CancelPanning and AllowPanningCancellation to NodifyEditor
1818
> - Added UpdateCuttingLine to NodifyEditor
19-
> - Added BeginSelecting, UpdateSelection, EndSelecting, CancelSelecting and AllowSelectionCancellation to NodifyEditor
19+
> - Added Select, BeginSelecting, UpdateSelection, EndSelecting, CancelSelecting and AllowSelectionCancellation to NodifyEditor
2020
> - Added IsDragging, BeginDragging, UpdateDragging, EndDragging and CancelDragging to NodifyEditor
2121
> - Added AlignSelection and AlignContainers methods to NodifyEditor
2222
> - Added HasCustomContextMenu dependency property to NodifyEditor and ItemContainer
@@ -29,7 +29,8 @@
2929
> - Fixed an issue where the ItemContainer was selected by releasing the mouse button on it, even when the mouse was not captured
3030
> - Fixed an issue where the Home button caused the editor to fail to display items when contained within a ScrollViewer
3131
> - Fixed an issue where connector optimization did not work when SelectedItems was not data-bound
32-
> - Fixed an issue where EditorCommands.Align caused multiple arrange invalidations, one for each aligned container
32+
> - Fixed EditorCommands.Align to perform a single arrange invalidation instead of one for each aligned container
33+
> - Fixed ItemContainer.Select and NodifyEditor.SelectArea to clear the existing selection and select the containers within the same transaction
3334
3435
#### **Version 6.6.0**
3536

Examples/Nodify.Shapes/Canvas/CanvasView.xaml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@
4444
ConnectionCompletedCommand="{Binding CreateConnectionCommand}"
4545
ItemsDragStartedCommand="{Binding MoveShapesStartedCommand}"
4646
ItemsDragCompletedCommand="{Binding MoveShapesCompletedCommand}"
47-
ItemsSelectStartedCommand="{Binding SelectShapesStartedCommand}"
48-
ItemsSelectCompletedCommand="{Binding SelectShapesCompletedCommand}"
4947
RemoveConnectionCommand="{Binding RemoveConnectionCommand}"
48+
SelectionChanged="Editor_SelectionChanged"
5049
MouseDown="Editor_MouseDown"
5150
MouseMove="Editor_MouseMove"
5251
MouseUp="Editor_MouseUp"
@@ -248,8 +247,6 @@
248247
</Style.Resources>
249248
<Setter Property="Location"
250249
Value="{Binding Location}" />
251-
<Setter Property="IsSelected"
252-
Value="{Binding IsSelected}" />
253250
<Setter Property="SelectedBrush"
254251
Value="{Binding BorderColor, Converter={StaticResource ColorToSolidColorBrushConverter}}" />
255252
<Setter Property="SelectedBorderThickness"

Examples/Nodify.Shapes/Canvas/CanvasView.xaml.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System;
1+
using Nodify.Shapes.Canvas.UndoRedo;
2+
using System;
23
using System.Collections.Generic;
4+
using System.Linq;
35
using System.Windows;
46
using System.Windows.Controls;
57
using System.Windows.Input;
@@ -103,5 +105,22 @@ private void Minimap_Zoom(object sender, ZoomEventArgs e)
103105
{
104106
Editor.ZoomAtPosition(e.Zoom, e.Location);
105107
}
108+
109+
private void Editor_SelectionChanged(object sender, SelectionChangedEventArgs e)
110+
{
111+
var canvasVM = (CanvasViewModel)DataContext;
112+
var selectedNodes = e.AddedItems.Cast<ShapeViewModel>().ToList();
113+
var deselectedNodes = e.RemovedItems.Cast<ShapeViewModel>().ToList();
114+
115+
string batchLabel = selectedNodes.Count > 0 ? "Select shapes" : "Deselect shapes";
116+
using (canvasVM.UndoRedo.Batch(batchLabel))
117+
{
118+
var selectNodes = new SelectShapesAction(selectedNodes, canvasVM);
119+
var deselectNodes = new DeselectShapesAction(deselectedNodes, canvasVM);
120+
121+
canvasVM.UndoRedo.Record(deselectNodes);
122+
canvasVM.UndoRedo.Record(selectNodes);
123+
}
124+
}
106125
}
107126
}

Examples/Nodify.Shapes/Canvas/CanvasViewModel.cs

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ public class CanvasViewModel : ObservableObject
2222

2323
public ICommand MoveShapesStartedCommand { get; }
2424
public ICommand MoveShapesCompletedCommand { get; }
25-
public ICommand SelectShapesStartedCommand { get; }
26-
public ICommand SelectShapesCompletedCommand { get; }
2725
public ICommand ResizeShapeStartedCommand { get; }
2826
public ICommand ResizeShapeCompletedCommand { get; }
2927
public ICommand CreateConnectionCommand { get; }
@@ -48,9 +46,6 @@ public CanvasViewModel()
4846
MoveShapesStartedCommand = new DelegateCommand(MoveShapesStartedHandler);
4947
MoveShapesCompletedCommand = new DelegateCommand(MoveShapesCompletedHandler);
5048

51-
SelectShapesStartedCommand = new DelegateCommand(SelectShapesStartedHandler);
52-
SelectShapesCompletedCommand = new DelegateCommand(SelectShapesCompletedHandler);
53-
5449
ResizeShapeStartedCommand = new DelegateCommand(ResizeShapeStartedHandler);
5550
ResizeShapeCompletedCommand = new DelegateCommand(ResizeShapeCompletedHandler);
5651

@@ -93,23 +88,6 @@ private void MoveShapesCompletedHandler()
9388
ShapeToolbar.Show();
9489
}
9590

96-
// TODO: This does not fire for single selection (ItemContainer click), therefore I added IsSelected to ShapeViewModel.
97-
private void SelectShapesStartedHandler()
98-
{
99-
// The workaround is to record all IsSelected changes into one history item using History.Pause and History.Resume
100-
UndoRedo.Pause("Select shapes");
101-
//UndoRedo.ExecuteAction(new SelectShapesAction(this));
102-
}
103-
104-
private void SelectShapesCompletedHandler()
105-
{
106-
UndoRedo.Resume();
107-
//if (UndoRedo.Current is SelectShapesAction selectShapes)
108-
//{
109-
// selectShapes.SaveSelection();
110-
//}
111-
}
112-
11391
private void ResizeShapeStartedHandler()
11492
{
11593
UndoRedo.ExecuteAction(new ResizeShapesAction(this));

Examples/Nodify.Shapes/Canvas/Shapes/ShapeViewModel.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ public abstract class ShapeViewModel : Undoable
99
{
1010
public ShapeViewModel(IActionsHistory history) : base(history)
1111
{
12-
RecordProperty<ShapeViewModel>(x => x.IsSelected);
1312
RecordProperty<ShapeViewModel>(x => x.Color);
1413
RecordProperty<ShapeViewModel>(x => x.Text);
1514
}
@@ -65,13 +64,6 @@ public string? Text
6564
set => SetProperty(ref _text, value);
6665
}
6766

68-
private bool _isSelected;
69-
public bool IsSelected
70-
{
71-
get => _isSelected;
72-
set => SetProperty(ref _isSelected, value);
73-
}
74-
7567
public ConnectorViewModel LeftConnector { get; } = new ConnectorViewModel(ConnectorPosition.Left);
7668
public ConnectorViewModel RightConnector { get; } = new ConnectorViewModel(ConnectorPosition.Right);
7769
public ConnectorViewModel TopConnector { get; } = new ConnectorViewModel(ConnectorPosition.Top);
Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,59 @@
11
using Nodify.UndoRedo;
22
using System.Collections.Generic;
3-
using System.Linq;
43

54
namespace Nodify.Shapes.Canvas.UndoRedo
65
{
76
public class SelectShapesAction : IAction
87
{
8+
public string? Label => "Select shapes";
9+
10+
private readonly IReadOnlyCollection<ShapeViewModel> _nodes;
911
private readonly CanvasViewModel _canvas;
10-
private readonly List<ShapeViewModel> _initialSelection;
11-
private List<ShapeViewModel>? _finalSelection;
1212

13-
public SelectShapesAction(CanvasViewModel canvas)
13+
public SelectShapesAction(IReadOnlyCollection<ShapeViewModel> nodes, CanvasViewModel canvas)
1414
{
15+
_nodes = nodes;
1516
_canvas = canvas;
16-
_initialSelection = _canvas.SelectedShapes.ToList();
1717
}
1818

19-
public string? Label => "Select shapes";
20-
2119
public void Execute()
2220
{
23-
if (_finalSelection != null)
24-
{
25-
_canvas.SelectedShapes.Clear();
26-
_canvas.SelectedShapes.AddRange(_finalSelection);
27-
}
21+
_canvas.SelectedShapes.AddRange(_nodes);
2822
}
2923

3024
public void Undo()
3125
{
32-
_canvas.SelectedShapes.Clear();
33-
_canvas.SelectedShapes.AddRange(_initialSelection);
26+
_canvas.SelectedShapes.RemoveRange(_nodes);
27+
}
28+
29+
public override string? ToString()
30+
=> Label;
31+
}
32+
33+
public class DeselectShapesAction : IAction
34+
{
35+
public string? Label => "Deselect shapes";
36+
37+
private readonly IReadOnlyCollection<ShapeViewModel> _nodes;
38+
private readonly CanvasViewModel _canvas;
39+
40+
public DeselectShapesAction(IReadOnlyCollection<ShapeViewModel> nodes, CanvasViewModel canvas)
41+
{
42+
_nodes = nodes;
43+
_canvas = canvas;
44+
}
45+
46+
public void Execute()
47+
{
48+
_canvas.SelectedShapes.RemoveRange(_nodes);
3449
}
3550

36-
public void SaveSelection()
51+
public void Undo()
3752
{
38-
_finalSelection = _canvas.SelectedShapes.ToList();
53+
_canvas.SelectedShapes.AddRange(_nodes);
3954
}
55+
56+
public override string? ToString()
57+
=> Label;
4058
}
4159
}

Nodify/ItemContainer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,7 @@ public void Select(SelectionType type)
364364
IsSelected = !IsSelected;
365365
break;
366366
case SelectionType.Replace:
367-
Editor.UnselectAll();
368-
IsSelected = true;
367+
Editor.Select(this);
369368
break;
370369
}
371370
}

Nodify/NodifyEditor.Selecting.cs

Lines changed: 55 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ private void OnItemsSelectStarted()
7676
ItemsSelectStartedCommand.Execute(DataContext);
7777
}
7878

79-
/// <summary>Invoked when a selection operation is started.</summary>
79+
/// <summary>Invoked when a selection operation is started (see <see cref="BeginSelecting(SelectionType)"/>).</summary>
8080
public ICommand? ItemsSelectStartedCommand
8181
{
8282
get => (ICommand?)GetValue(ItemsSelectStartedCommandProperty);
8383
set => SetValue(ItemsSelectStartedCommandProperty, value);
8484
}
8585

86-
/// <summary>Invoked when a selection operation is completed.</summary>
86+
/// <summary>Invoked when a selection operation is completed (see <see cref="EndSelecting"/>).</summary>
8787
public ICommand? ItemsSelectCompletedCommand
8888
{
8989
get => (ICommand?)GetValue(ItemsSelectCompletedCommandProperty);
@@ -216,42 +216,6 @@ protected internal IReadOnlyList<ItemContainer> SelectedContainers
216216

217217
#region Selection
218218

219-
internal void ApplyPreviewingSelection()
220-
{
221-
Debug.Assert(IsSelecting);
222-
223-
ItemCollection items = Items;
224-
IList selected = base.SelectedItems;
225-
226-
BeginUpdateSelectedItems();
227-
for (var i = 0; i < items.Count; i++)
228-
{
229-
var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);
230-
if (container.IsPreviewingSelection == true && container.IsSelectable)
231-
{
232-
selected.Add(items[i]);
233-
}
234-
else if (container.IsPreviewingSelection == false)
235-
{
236-
selected.Remove(items[i]);
237-
}
238-
container.IsPreviewingSelection = null;
239-
}
240-
EndUpdateSelectedItems();
241-
}
242-
243-
internal void ClearPreviewingSelection()
244-
{
245-
Debug.Assert(IsSelecting);
246-
247-
ItemCollection items = Items;
248-
for (var i = 0; i < items.Count; i++)
249-
{
250-
var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);
251-
container.IsPreviewingSelection = null;
252-
}
253-
}
254-
255219
/// <summary>
256220
/// Inverts the <see cref="ItemContainer"/>s selection in the specified <paramref name="area"/>.
257221
/// </summary>
@@ -293,16 +257,16 @@ public void InvertSelection(Rect area, bool fit = false)
293257
/// <param name="fit">True to check if the <paramref name="area"/> contains the <see cref="ItemContainer"/>. <br />False to check if <paramref name="area"/> intersects the <see cref="ItemContainer"/>.</param>
294258
public void SelectArea(Rect area, bool append = false, bool fit = false)
295259
{
260+
IsSelecting = true;
261+
BeginUpdateSelectedItems();
262+
263+
IList selected = base.SelectedItems;
296264
if (!append)
297265
{
298-
UnselectAll();
266+
selected.Clear();
299267
}
300268

301269
ItemCollection items = Items;
302-
IList selected = base.SelectedItems;
303-
304-
IsSelecting = true;
305-
BeginUpdateSelectedItems();
306270
for (var i = 0; i < items.Count; i++)
307271
{
308272
var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);
@@ -311,10 +275,24 @@ public void SelectArea(Rect area, bool append = false, bool fit = false)
311275
selected.Add(items[i]);
312276
}
313277
}
278+
314279
EndUpdateSelectedItems();
315280
IsSelecting = false;
316281
}
317282

283+
/// <summary>
284+
/// Clears the current selection and selects the specified <see cref="ItemContainer"/> within the same selection transaction.
285+
/// </summary>
286+
/// <param name="container"></param>
287+
public void Select(ItemContainer container)
288+
{
289+
BeginUpdateSelectedItems();
290+
var selected = base.SelectedItems;
291+
selected.Clear();
292+
selected.Add(container.DataContext);
293+
EndUpdateSelectedItems();
294+
}
295+
318296
/// <summary>
319297
/// Unselect the <see cref="ItemContainer"/>s in the specified <paramref name="area"/>.
320298
/// </summary>
@@ -359,6 +337,7 @@ public void SelectAllConnections()
359337
selector.SelectAll();
360338
}
361339
}
340+
362341
/// <summary>
363342
/// Initiates a selection operation from the specified location.
364343
/// </summary>
@@ -420,7 +399,7 @@ public void EndSelecting()
420399
/// <remarks>This method has no effect if there's no selection operation in progress.</remarks>
421400
public void CancelSelecting()
422401
{
423-
if(!AllowSelectionCancellation)
402+
if (!AllowSelectionCancellation)
424403
{
425404
EndSelecting();
426405
return;
@@ -433,6 +412,38 @@ public void CancelSelecting()
433412
}
434413
}
435414

415+
private void ApplyPreviewingSelection()
416+
{
417+
ItemCollection items = Items;
418+
IList selected = base.SelectedItems;
419+
420+
BeginUpdateSelectedItems();
421+
for (var i = 0; i < items.Count; i++)
422+
{
423+
var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);
424+
if (container.IsPreviewingSelection == true && container.IsSelectable)
425+
{
426+
selected.Add(items[i]);
427+
}
428+
else if (container.IsPreviewingSelection == false)
429+
{
430+
selected.Remove(items[i]);
431+
}
432+
container.IsPreviewingSelection = null;
433+
}
434+
EndUpdateSelectedItems();
435+
}
436+
437+
private void ClearPreviewingSelection()
438+
{
439+
ItemCollection items = Items;
440+
for (var i = 0; i < items.Count; i++)
441+
{
442+
var container = (ItemContainer)ItemContainerGenerator.ContainerFromIndex(i);
443+
container.IsPreviewingSelection = null;
444+
}
445+
}
446+
436447
#endregion
437448

438449
#region Selection Handlers

0 commit comments

Comments
 (0)