Skip to content

Commit 9686b51

Browse files
authored
Checkbox selection (#35)
* Refactor and enhance AdvancedSearchControl - Added `IsReadOnly` property to `DataGrid` columns to prevent editing. - Changed `DataGrid` selection mode to `Single` and added checkboxes. - Enhanced context menu with "Select All" and "Deselect All" options. - Simplified button command bindings and removed redundant parameters. - Refactored `DropdownButton_Click` for cleaner logic. - Introduced `SelectAll` and `DeselectAll` commands in the view model. - Simplified `AddSelectedToGraph` logic with a helper method. - Added `IsSelected` property to `SearchItemViewModel` for selection tracking. - Fixed syntax issue in `CodeGraphSerializer` for `Split` method. - Improved maintainability and usability of the control and view model. * String constants * Add Ctrl+A support and improve DataGrid functionality Enhanced the `AdvancedSearchControl` with the following updates: - Added `PreviewKeyDown` event to `DataGrid` to enable "Ctrl+A" for selecting all items. - Updated `CopyToClipboardCommand` to include `CommandParameter` for better context handling. - Enabled sorting in `DataGridTemplateColumn` using `SortMemberPath="IsSelected"`. - Refactored `OnCopyToClipboard` to use `SearchItemViewModel` for type safety. - Modified `GetSelectedCodeElements` to include all items, even non-visible ones. - Improved documentation for better clarity and maintainability.
1 parent bb9cb71 commit 9686b51

File tree

7 files changed

+132
-54
lines changed

7 files changed

+132
-54
lines changed

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchControl.xaml

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,26 @@
5252
dd:DragDrop.IsDragSource="True"
5353
x:Name="SearchDataGrid"
5454
Style="{StaticResource ReadOnlyGrid}"
55+
IsReadOnly="False"
5556
ItemsSource="{Binding FilteredItems}"
5657
AutoGenerateColumns="False"
5758
GridLinesVisibility="Horizontal"
58-
SelectionMode="Extended"
59+
SelectionMode="Single"
60+
PreviewKeyDown="SearchDataGrid_PreviewKeyDown"
5961
Margin="5,0,5,0">
6062
<DataGrid.ContextMenu>
6163
<ContextMenu>
64+
<MenuItem Header="{x:Static resources:Strings.SelectAllVisible_Label}"
65+
Command="{Binding SelectAllCommand}" />
66+
<MenuItem Header="{x:Static resources:Strings.DeselectAllVisible_Label}"
67+
Command="{Binding DeselectAllCommand}" />
68+
<Separator />
69+
6270
<MenuItem Header="{x:Static resources:Strings.AddSelectedToGraph_Label}"
63-
Command="{Binding AddSelectedToGraphCommand}"
64-
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
71+
Command="{Binding AddSelectedToGraphCommand}" />
6572
<MenuItem
6673
Header="{x:Static resources:Strings.AddSelectedToGraphCollapsed_Label}"
67-
Command="{Binding AddSelectedToGraphCollapsedCommand}"
68-
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
74+
Command="{Binding AddSelectedToGraphCollapsedCommand}" />
6975

7076
<Separator />
7177
<MenuItem Header="{x:Static resources:Strings.Partition}"
@@ -75,7 +81,7 @@
7581

7682
<MenuItem Header="{x:Static resources:Strings.CopyFullQualifiedNameToClipboard}"
7783
Command="{Binding CopyToClipboardCommand}"
78-
CommandParameter="{Binding PlacementTarget.SelectedItems, RelativeSource={RelativeSource AncestorType=ContextMenu}}">
84+
CommandParameter="{Binding PlacementTarget.SelectedItem, RelativeSource={RelativeSource AncestorType=ContextMenu}}" >
7985
<MenuItem.Icon>
8086
<Image Source="/Resources/copy_fqn_16.png" />
8187
</MenuItem.Icon>
@@ -85,8 +91,26 @@
8591
</ContextMenu>
8692
</DataGrid.ContextMenu>
8793
<DataGrid.Columns>
94+
95+
<!-- Don't use the DataGridCheckBoxColumn.
96+
It requires to click twice on a checkbox to check it -->
97+
<DataGridTemplateColumn SortMemberPath="IsSelected" CanUserSort="True">
98+
<DataGridTemplateColumn.CellTemplate>
99+
<DataTemplate>
100+
<CheckBox HorizontalAlignment="Center"
101+
Width="30"
102+
IsChecked="{Binding Path=IsSelected, UpdateSourceTrigger=PropertyChanged}" />
103+
</DataTemplate>
104+
</DataGridTemplateColumn.CellTemplate>
105+
</DataGridTemplateColumn>
106+
107+
<!--<DataGridCheckBoxColumn Header=""
108+
Binding="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}"
109+
Width="30" />-->
110+
88111
<DataGridTemplateColumn Header="{x:Static resources:Strings.Type_Label}"
89-
Width="Auto">
112+
Width="Auto"
113+
IsReadOnly="True">
90114
<DataGridTemplateColumn.CellTemplate>
91115
<DataTemplate>
92116
<Image Source="{Binding Icon}" Width="16" Height="16"
@@ -97,12 +121,14 @@
97121

98122
<DataGridTextColumn Header="{x:Static resources:Strings.Name_Label}"
99123
Binding="{Binding Name}"
100-
Width="Auto" />
124+
Width="Auto"
125+
IsReadOnly="True" />
101126

102127

103128
<DataGridTextColumn Header="{x:Static resources:Strings.FullPath_Label}"
104129
Binding="{Binding FullPath}"
105-
Width="Auto" />
130+
Width="Auto"
131+
IsReadOnly="True" />
106132
</DataGrid.Columns>
107133
</DataGrid>
108134

@@ -118,7 +144,6 @@
118144
<Button Grid.Column="0"
119145
Content="{x:Static resources:Strings.AddSelectedToGraph_Label}"
120146
Command="{Binding AddSelectedToGraphCommand}"
121-
CommandParameter="{Binding SelectedItems, ElementName=SearchDataGrid}"
122147
FontWeight="Bold"
123148
Padding="15,5" />
124149

@@ -133,8 +158,7 @@
133158
<ContextMenu>
134159
<MenuItem
135160
Header="{x:Static resources:Strings.AddSelectedToGraphCollapsed_Label}"
136-
Command="{Binding AddSelectedToGraphCollapsedCommand}"
137-
CommandParameter="{Binding Path=Tag.SelectedItems, RelativeSource={RelativeSource Self}}" />
161+
Command="{Binding AddSelectedToGraphCollapsedCommand}" />
138162
</ContextMenu>
139163
</Button.ContextMenu>
140164
<Button.Style>

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchControl.xaml.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System.Windows;
22
using System.Windows.Controls;
33
using System.Windows.Controls.Primitives;
4+
using System.Windows.Input;
45

56
namespace CSharpCodeAnalyst.Areas.AdvancedSearchArea;
67

@@ -13,13 +14,23 @@ public AdvancedSearchControl()
1314

1415
private void DropdownButton_Click(object sender, RoutedEventArgs e)
1516
{
16-
if (sender is Button { ContextMenu: not null } button
17-
&& button.ContextMenu.Items[0] is MenuItem item)
17+
if (sender is Button { ContextMenu: not null } button)
1818
{
1919
button.ContextMenu.PlacementTarget = button;
20-
item.Tag = SearchDataGrid;
2120
button.ContextMenu.Placement = PlacementMode.Bottom;
2221
button.ContextMenu.IsOpen = true;
2322
}
2423
}
24+
25+
private void SearchDataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
26+
{
27+
if (e.Key == Key.A && (Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
28+
{
29+
if (sender is DataGrid {DataContext: AdvancedSearchViewModel viewModel})
30+
{
31+
e.Handled = true;
32+
viewModel.SelectAllCommand.Execute(null);
33+
}
34+
}
35+
}
2536
}

CSharpCodeAnalyst/Areas/AdvancedSearchArea/AdvancedSearchViewModel.cs

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@ public AdvancedSearchViewModel(MessageBus messaging)
3939
};
4040

4141
ClearSearchCommand = new WpfCommand(ClearSearch);
42-
AddSelectedToGraphCommand = new WpfCommand<object>(AddSelectedToGraph);
43-
AddSelectedToGraphCollapsedCommand = new WpfCommand<object>(AddSelectedToGraphCollapsed);
42+
AddSelectedToGraphCommand = new WpfCommand(AddSelectedToGraph);
43+
AddSelectedToGraphCollapsedCommand = new WpfCommand(AddSelectedToGraphCollapsed);
4444
PartitionCommand = new WpfCommand<SearchItemViewModel>(OnPartition, CanPartition);
45-
CopyToClipboardCommand = new WpfCommand<object>(OnCopyToClipboard);
45+
CopyToClipboardCommand = new WpfCommand<SearchItemViewModel>(OnCopyToClipboard);
46+
SelectAllCommand = new WpfCommand(SelectAll);
47+
DeselectAllCommand = new WpfCommand(DeselectAll);
4648
}
4749

4850
public ObservableCollection<SearchItemViewModel> AllItems
@@ -83,14 +85,14 @@ public string SearchText
8385
public ICommand AddSelectedToGraphCollapsedCommand { get; }
8486
public ICommand PartitionCommand { get; }
8587
public ICommand CopyToClipboardCommand { get; }
88+
public ICommand SelectAllCommand { get; }
89+
public ICommand DeselectAllCommand { get; }
8690

8791
public event PropertyChangedEventHandler? PropertyChanged;
8892

89-
private static void OnCopyToClipboard(object? items)
93+
private void OnCopyToClipboard(SearchItemViewModel item)
9094
{
91-
var elements = GetSelectedCodeElements(items);
92-
93-
var text = string.Join(Environment.NewLine, elements.Select(e => e.FullName));
95+
var text = item.CodeElement?.FullName;
9496
if (string.IsNullOrEmpty(text))
9597
{
9698
return;
@@ -175,52 +177,52 @@ private void ClearSearch()
175177
ExecuteSearchInternal(); // Immediately show all items
176178
}
177179

178-
private void AddSelectedToGraph(object? selectedItems)
179-
{
180-
AddSelectedToGraphInternal(selectedItems, false);
181-
}
182-
183-
private void AddSelectedToGraphCollapsed(object? selectedItems)
180+
private void SelectAll()
184181
{
185-
AddSelectedToGraphInternal(selectedItems, true);
182+
foreach (var item in FilteredItems)
183+
{
184+
item.IsSelected = true;
185+
}
186186
}
187187

188-
private void AddSelectedToGraphInternal(object? selectedItems, bool addCollapsed)
188+
private void DeselectAll()
189189
{
190-
var codeElements = GetSelectedCodeElements(selectedItems);
191-
192-
if (codeElements.Count > 0)
190+
foreach (var item in FilteredItems)
193191
{
194-
_messaging.Publish(new AddNodeToGraphRequest(codeElements, addCollapsed));
192+
item.IsSelected = false;
195193
}
196194
}
197195

198-
private static List<CodeElement> GetSelectedCodeElements(object? selectedItems)
196+
private void AddSelectedToGraph()
199197
{
200-
var elements = new List<CodeElement>();
198+
AddSelectedToGraphInternal(false);
199+
}
201200

202-
if (selectedItems is null)
203-
{
204-
return elements;
205-
}
201+
private void AddSelectedToGraphCollapsed()
202+
{
203+
AddSelectedToGraphInternal(true);
204+
}
206205

207-
if (selectedItems is SearchItemViewModel { CodeElement: not null } item)
208-
{
209-
elements.Add(item.CodeElement);
210-
return elements;
211-
}
206+
private void AddSelectedToGraphInternal(bool addCollapsed)
207+
{
208+
var codeElements = GetSelectedCodeElements();
212209

213-
if (selectedItems is IList list)
210+
if (codeElements.Count > 0)
214211
{
215-
var codeElements = list.OfType<SearchItemViewModel>()
216-
.Where(i => i.CodeElement != null)
217-
.Select(i => i.CodeElement!)
218-
.ToList();
219-
220-
elements.AddRange(codeElements);
212+
_messaging.Publish(new AddNodeToGraphRequest(codeElements, addCollapsed));
221213
}
214+
}
222215

223-
return elements;
216+
/// <summary>
217+
/// Gets selected code elements from all items, including the
218+
/// currently non-visible.
219+
/// </summary>
220+
private List<CodeElement> GetSelectedCodeElements()
221+
{
222+
return AllItems
223+
.Where(item => item is { IsSelected: true, CodeElement: not null })
224+
.Select(item => item.CodeElement!)
225+
.ToList();
224226
}
225227

226228
private void OnPropertyChanged(string propertyName)

CSharpCodeAnalyst/Areas/AdvancedSearchArea/SearchItemViewModel.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,26 @@ namespace CSharpCodeAnalyst.Areas.AdvancedSearchArea;
99
[DebuggerDisplay("{Type} {Name} - {FullPath}")]
1010
public sealed class SearchItemViewModel : INotifyPropertyChanged
1111
{
12+
private bool _isSelected;
13+
1214
public string Name { get; set; } = string.Empty;
1315
public string Type { get; set; } = string.Empty;
1416
public string FullPath { get; set; } = string.Empty;
1517
public CodeElement? CodeElement { get; set; }
1618

19+
public bool IsSelected
20+
{
21+
get => _isSelected;
22+
set
23+
{
24+
if (_isSelected != value)
25+
{
26+
_isSelected = value;
27+
OnPropertyChanged(nameof(IsSelected));
28+
}
29+
}
30+
}
31+
1732
public BitmapImage? Icon
1833
{
1934
get => CodeElement != null ? CodeElementIconMapper.GetIcon(CodeElement.ElementType) : null;

CSharpCodeAnalyst/Resources/Strings.Designer.cs

Lines changed: 19 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CSharpCodeAnalyst/Resources/Strings.resx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -955,4 +955,11 @@ ISOLATE: MyApp.Domain.**</value>
955955
<value>Import plain text ...</value>
956956
</data>
957957

958+
<data name="SelectAllVisible_Label" xml:space="preserve">
959+
<value>Select all visible</value>
960+
</data>
961+
<data name="DeselectAllVisible_Label" xml:space="preserve">
962+
<value>Deselect all visible</value>
963+
</data>
964+
958965
</root>

Contracts/Graph/CodeGraphSerializer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ private static List<string> GetAttributeFlags(RelationshipAttribute attr)
133133
public static CodeGraph Deserialize(string content)
134134
{
135135
var graph = new CodeGraph();
136-
var lines = content.Split(['\r', '\n'], StringSplitOptions.RemoveEmptyEntries).Select(l => l.TrimEnd()).ToArray();
136+
var lines = content.Split(new[]{'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries).Select(l => l.TrimEnd()).ToArray();
137137

138138
var currentLine = 0;
139139
var parentIds = new Dictionary<string, string>(); // childId -> parentId

0 commit comments

Comments
 (0)