Skip to content

Commit da8a8f2

Browse files
committed
Add sorting function to files list
Files and folders can now be sorted by clicking on the column headers or by using the right-click context menu. The sorting order mimics Windows Explorer, by grouping the folders and files separately first, then applying the relevant sort on the specified column, then finally sorting by name. Also: - Improves right-click context menu action grouping - Removes RowIndex from ListedItem.cs as it is not a reliable way to access items (indices change) after sorting is implemented - Refactors code to work around the absence of RowIndex - Other minor code refactoring
1 parent 73c879b commit da8a8f2

File tree

7 files changed

+171
-47
lines changed

7 files changed

+171
-47
lines changed

Files UWP/Filesystem/ItemViewModel.cs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,58 @@ public void CancelLoadAndClearFiles()
250250

251251
}
252252

253+
public void OrderFiles(string orderBy, bool ascending)
254+
{
255+
Func<ListedItem, object> orderFunc;
256+
object orderByNameFunc(ListedItem item) => item.FileName;
257+
switch (orderBy)
258+
{
259+
case "Name":
260+
orderFunc = orderByNameFunc;
261+
break;
262+
case "Date":
263+
orderFunc = item => item.FileDateReal;
264+
break;
265+
case "Type":
266+
orderFunc = item => item.FileType;
267+
break;
268+
case "Size":
269+
orderFunc = item => item.FileSizeBytes;
270+
break;
271+
default:
272+
return;
273+
}
274+
275+
// In ascending order, show folders first, then files.
276+
// So, we use != "Folder" to make the value for "Folder" = 0, and for the rest, 1.
277+
Func<ListedItem, bool> folderThenFile = listedItem => listedItem.FileType != "Folder";
278+
IOrderedEnumerable<ListedItem> ordered;
279+
List<ListedItem> orderedList;
280+
281+
if (ascending)
282+
ordered = _filesAndFolders.OrderBy(folderThenFile).ThenBy(orderFunc);
283+
else
284+
{
285+
if (orderBy == "Type")
286+
ordered = _filesAndFolders.OrderBy(folderThenFile).ThenByDescending(orderFunc);
287+
else
288+
ordered = _filesAndFolders.OrderByDescending(folderThenFile).ThenByDescending(orderFunc);
289+
}
290+
291+
// Further order by name if applicable
292+
if (orderBy != "Name")
293+
{
294+
if (ascending)
295+
ordered = ordered.ThenBy(orderByNameFunc);
296+
else
297+
ordered = ordered.ThenByDescending(orderByNameFunc);
298+
}
299+
orderedList = ordered.ToList();
300+
_filesAndFolders.Clear();
301+
foreach (ListedItem i in orderedList)
302+
_filesAndFolders.Add(i);
303+
}
304+
253305
public static T GetCurrentSelectedTabInstance<T>()
254306
{
255307
Frame rootFrame = Window.Current.Content as Frame;
@@ -517,7 +569,7 @@ private async Task AddFolder(StorageFolder folder)
517569
FilePath = folder.Path,
518570
EmptyImgVis = Visibility.Collapsed,
519571
FileSize = null,
520-
RowIndex = _filesAndFolders.Count
572+
FileSizeBytes = 0
521573
});
522574
if((App.selectedTabInstance.accessibleContentFrame.Content as GenericFileBrowser) != null)
523575
{
@@ -538,6 +590,7 @@ private async Task AddFile(StorageFile file)
538590
var itemDate = basicProperties.DateModified;
539591
var itemPath = file.Path;
540592
var itemSize = ByteSize.FromBytes(basicProperties.Size).ToString();
593+
var itemSizeBytes = basicProperties.Size;
541594
var itemType = file.DisplayType;
542595
var itemFolderImgVis = Visibility.Collapsed;
543596
var itemFileExtension = file.FileType;
@@ -616,7 +669,7 @@ private async Task AddFile(StorageFile file)
616669
FileType = itemType,
617670
FilePath = itemPath,
618671
FileSize = itemSize,
619-
RowIndex = _filesAndFolders.Count
672+
FileSizeBytes = itemSizeBytes
620673
});
621674

622675
if(App.selectedTabInstance.accessibleContentFrame.SourcePageType == typeof(GenericFileBrowser))

Files UWP/Filesystem/ListedItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class ListedItem
1818
public string DotFileExtension { get; set; }
1919
public string FilePath { get; set; }
2020
public string FileSize { get; set; }
21-
public int RowIndex { get; set; }
21+
public ulong FileSizeBytes { get; set; }
2222

2323
public DateTimeOffset FileDateReal
2424
{

Files UWP/GenericFileBrowser.xaml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,24 @@
154154
<Grid Background="Transparent" x:Name="RootGrid">
155155
<Grid.ContextFlyout>
156156
<MenuFlyout MenuFlyoutPresenterStyle="{StaticResource MenuFlyoutFluentThemeResources}">
157+
<MenuFlyoutSubItem Text="Sort by" x:Name="SortByEmptySpace">
158+
<MenuFlyoutSubItem.Icon>
159+
<FontIcon Glyph="&#xE8CB;"/>
160+
</MenuFlyoutSubItem.Icon>
161+
<uilib:RadioMenuFlyoutItem Text="Name" GroupName="SortGroup" IsChecked="{x:Bind IsSortedByName, Mode=TwoWay}"/>
162+
<uilib:RadioMenuFlyoutItem Text="Date modified" GroupName="SortGroup" IsChecked="{x:Bind IsSortedByDate, Mode=TwoWay}"/>
163+
<uilib:RadioMenuFlyoutItem Text="Type" GroupName="SortGroup" IsChecked="{x:Bind IsSortedByType, Mode=TwoWay}"/>
164+
<uilib:RadioMenuFlyoutItem Text="Size" GroupName="SortGroup" IsChecked="{x:Bind IsSortedBySize, Mode=TwoWay}"/>
165+
<MenuFlyoutSeparator/>
166+
<uilib:RadioMenuFlyoutItem Text="Ascending" GroupName="SortOrderGroup" IsChecked="{x:Bind IsSortedAscending, Mode=TwoWay}"/>
167+
<uilib:RadioMenuFlyoutItem Text="Descending" GroupName="SortOrderGroup" IsChecked="{x:Bind IsSortedDescending, Mode=TwoWay}"/>
168+
</MenuFlyoutSubItem>
157169
<MenuFlyoutItem Text="Refresh" x:Name="RefreshEmptySpace">
158170
<MenuFlyoutItem.Icon>
159171
<FontIcon Glyph="&#xE72C;"/>
160172
</MenuFlyoutItem.Icon>
161173
</MenuFlyoutItem>
174+
<MenuFlyoutSeparator/>
162175
<MenuFlyoutItem Text="Paste" x:Name="PasteEmptySpace" IsEnabled="{x:Bind Mode=TwoWay, Path=local:App.PS.isEnabled, UpdateSourceTrigger=PropertyChanged}">
163176
<MenuFlyoutItem.Icon>
164177
<FontIcon Glyph="&#xE77F;"/>
@@ -199,7 +212,7 @@
199212
</Grid.ContextFlyout>
200213
<ProgressBar x:Name="progBar" Height="10" VerticalAlignment="Top" IsIndeterminate="True"/>
201214
<TextBlock Visibility="{x:Bind Mode=TwoWay, Path=TextState.isVisible, UpdateSourceTrigger=PropertyChanged}" x:Name="EmptyText" HorizontalAlignment="Center" Text="This folder is empty." TextWrapping="Wrap" VerticalAlignment="Top" Margin="0,125,0,0"/>
202-
<controls:DataGrid ItemsSource="{x:Bind viewModelInstance.FilesAndFolders}" ScrollViewer.IsScrollInertiaEnabled="True" ClipboardCopyMode="None" RowDetailsVisibilityMode="Collapsed" AllowDrop="True" Drop="AllView_DropAsync" DragLeave="AllView_DragLeave" DragStarting="AllView_DragStarting" SelectionChanged="AllView_SelectionChanged" Margin="24,24,0,0" Grid.Row="3" CellEditEnded="AllView_CellEditEnded" FocusVisualPrimaryThickness="0" SelectionMode="Extended" IsDoubleTapEnabled="True" x:FieldModifier="public" x:Name="AllView" AutoGenerateColumns="False" CanDrag="True" DragOver="AllView_DragOver" IsRightTapEnabled="True" CanUserReorderColumns="False" IsReadOnly="True" HorizontalAlignment="Left">
215+
<controls:DataGrid ItemsSource="{x:Bind viewModelInstance.FilesAndFolders}" ScrollViewer.IsScrollInertiaEnabled="True" ClipboardCopyMode="None" RowDetailsVisibilityMode="Collapsed" AllowDrop="True" Drop="AllView_DropAsync" DragLeave="AllView_DragLeave" DragStarting="AllView_DragStarting" SelectionChanged="AllView_SelectionChanged" Margin="24,24,0,0" Grid.Row="3" CellEditEnded="AllView_CellEditEnded" FocusVisualPrimaryThickness="0" SelectionMode="Extended" IsDoubleTapEnabled="True" x:FieldModifier="public" x:Name="AllView" AutoGenerateColumns="False" CanDrag="True" DragOver="AllView_DragOver" IsRightTapEnabled="True" CanUserReorderColumns="False" CanUserSortColumns="True" Sorting="AllView_Sorting" IsReadOnly="True" HorizontalAlignment="Left">
203216
<controls:DataGrid.Resources>
204217
<SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="Transparent"/>
205218
<SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="Transparent"/>
@@ -254,7 +267,6 @@
254267
<KeyboardAccelerator Modifiers="Control" Key="S"/>
255268
</MenuFlyoutItem.KeyboardAccelerators>
256269
</MenuFlyoutItem>
257-
258270
<MenuFlyoutSeparator/>
259271
<MenuFlyoutItem Text="Delete" x:Name="DeleteItem" >
260272
<MenuFlyoutItem.Icon>
@@ -289,22 +301,17 @@
289301
<KeyboardAccelerator Modifiers="Control" Key="C"/>
290302
</MenuFlyoutItem.KeyboardAccelerators>
291303
</MenuFlyoutItem>
292-
293304
<MenuFlyoutSeparator/>
294305
<MenuFlyoutItem Text="Pin to sidebar" x:Name="SidebarPinItem">
295306
<MenuFlyoutItem.Icon>
296307
<SymbolIcon Symbol="Pin"/>
297308
</MenuFlyoutItem.Icon>
298-
299309
</MenuFlyoutItem>
300-
301310
<MenuFlyoutItem Text="Properties" x:Name="PropertiesItem">
302311
<MenuFlyoutItem.Icon>
303312
<FontIcon Glyph="&#xE946;"/>
304313
</MenuFlyoutItem.Icon>
305-
306314
</MenuFlyoutItem>
307-
308315
</MenuFlyout>
309316
</Setter.Value>
310317
</Setter>
@@ -320,7 +327,7 @@
320327
</controls:DataGrid.CellStyle>
321328

322329
<controls:DataGrid.Columns>
323-
<controls:DataGridTemplateColumn DisplayIndex="0" IsReadOnly="True">
330+
<controls:DataGridTemplateColumn DisplayIndex="0" x:Name="iconColumn" IsReadOnly="True">
324331
<controls:DataGridTemplateColumn.CellTemplate>
325332
<DataTemplate>
326333
<Grid x:Name="Icon" Margin="0, 0, 0, 0">
@@ -332,9 +339,9 @@
332339
</DataTemplate>
333340
</controls:DataGridTemplateColumn.CellTemplate>
334341
</controls:DataGridTemplateColumn>
335-
<controls:DataGridTextColumn DisplayIndex="1" IsReadOnly="True" Header="Name" Width="275" Binding="{Binding FileName}" Tag="Name"/>
336-
<controls:DataGridTextColumn DisplayIndex="2" IsReadOnly="True" Header="Date modified" Width="Auto" Binding="{Binding FileDate}" Tag="Date"/>
337-
<controls:DataGridTextColumn DisplayIndex="3" IsReadOnly="True" Header="Type" Width="150" Binding="{Binding FileType}" Tag="Type"/>
342+
<controls:DataGridTextColumn DisplayIndex="1" x:Name="nameColumn" IsReadOnly="True" Header="Name" Width="275" Binding="{Binding FileName}" Tag="Name"/>
343+
<controls:DataGridTextColumn DisplayIndex="2" x:Name="dateColumn" IsReadOnly="True" Header="Date modified" Width="Auto" Binding="{Binding FileDate}" Tag="Date"/>
344+
<controls:DataGridTextColumn DisplayIndex="3" x:Name="typeColumn" IsReadOnly="True" Header="Type" Width="150" Binding="{Binding FileType}" Tag="Type"/>
338345
<controls:DataGridTextColumn DisplayIndex="4" x:Name="sizeColumn" IsReadOnly="True" Header="Size" Width="Auto" MinWidth="100" Binding="{Binding FileSize}" Tag="Size"/>
339346
</controls:DataGrid.Columns>
340347
</controls:DataGrid>

Files UWP/GenericFileBrowser.xaml.cs

Lines changed: 77 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@
1616

1717
namespace Files
1818
{
19-
public sealed partial class GenericFileBrowser : Page
19+
public sealed partial class GenericFileBrowser : Page, INotifyPropertyChanged
2020
{
2121
public TextBlock emptyTextGFB;
2222
public TextBlock textBlock;
2323
public DataGrid data;
24+
public DataGridColumn sortedColumn;
2425
public MenuFlyout context;
2526
public MenuFlyout emptySpaceContext;
2627
public MenuFlyout HeaderContextMenu;
@@ -34,6 +35,9 @@ public sealed partial class GenericFileBrowser : Page
3435
public ProgressBar progressBar;
3536
ItemViewModel viewModelInstance;
3637
ProHome tabInstance;
38+
bool isSortedAscending;
39+
40+
public event PropertyChangedEventHandler PropertyChanged;
3741

3842
public EmptyFolderTextState TextState { get; set; } = new EmptyFolderTextState();
3943

@@ -45,6 +49,9 @@ public GenericFileBrowser()
4549
progressBar = progBar;
4650
progressBar.Visibility = Visibility.Collapsed;
4751
data = AllView;
52+
sortedColumn = nameColumn;
53+
sortedColumn.SortDirection = DataGridSortDirection.Ascending;
54+
isSortedAscending = true;
4855
context = RightClickContextMenu;
4956
HeaderContextMenu = HeaderRightClickMenu;
5057
grid = RootGrid;
@@ -196,7 +203,6 @@ protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
196203
//this.Bindings.StopTracking();
197204
}
198205

199-
200206
private void AllView_DragOver(object sender, DragEventArgs e)
201207
{
202208
e.AcceptedOperation = DataPackageOperation.Copy;
@@ -314,7 +320,7 @@ private void AllView_DragLeave(object sender, DragEventArgs e)
314320

315321
private void RightClickContextMenu_Opened(object sender, object e)
316322
{
317-
var selectedDataItem = tabInstance.instanceViewModel.FilesAndFolders[AllView.SelectedIndex];
323+
var selectedDataItem = AllView.SelectedItem as ListedItem;
318324
if (selectedDataItem.FileType != "Folder" || AllView.SelectedItems.Count > 1)
319325
{
320326
SidebarPinItem.Visibility = Visibility.Collapsed;
@@ -329,12 +335,78 @@ private void RightClickContextMenu_Opened(object sender, object e)
329335
}
330336
}
331337

338+
private void AllView_Sorting(object sender, DataGridColumnEventArgs e)
339+
{
340+
if (e.Column == iconColumn)
341+
return;
342+
if (sortedColumn == e.Column)
343+
isSortedAscending = e.Column.SortDirection == DataGridSortDirection.Descending;
344+
else
345+
{
346+
isSortedAscending = true;
347+
}
348+
349+
NotifyPropertyChanged("IsSortedAscending");
350+
NotifyPropertyChanged("IsSortedDescending");
351+
352+
SortBy(e.Column);
353+
}
354+
355+
private void SortBy(DataGridColumn column)
356+
{
357+
var selectedItems = data.SelectedItems;
358+
359+
viewModelInstance.OrderFiles(column.Tag.ToString(), isSortedAscending);
360+
361+
// Remove arrow on previous sorted column
362+
sortedColumn.SortDirection = null;
363+
column.SortDirection = isSortedAscending ? DataGridSortDirection.Ascending : DataGridSortDirection.Descending;
364+
sortedColumn = column;
365+
366+
if (selectedItems.Count == 1)
367+
{
368+
data.SelectedItem = selectedItems[0];
369+
}
370+
}
371+
372+
private bool IsSortedByName { get { return sortedColumn == nameColumn; } set { if (value) SortBy(nameColumn); } }
373+
private bool IsSortedByDate { get { return sortedColumn == dateColumn; } set { if (value) SortBy(dateColumn); } }
374+
private bool IsSortedByType { get { return sortedColumn == typeColumn; } set { if (value) SortBy(typeColumn); } }
375+
private bool IsSortedBySize { get { return sortedColumn == sizeColumn; } set { if (value) SortBy(sizeColumn); } }
376+
private bool IsSortedAscending
377+
{
378+
get
379+
{
380+
return isSortedAscending;
381+
}
382+
set
383+
{
384+
isSortedAscending = value;
385+
if (value) SortBy(sortedColumn);
386+
}
387+
}
388+
389+
private bool IsSortedDescending
390+
{
391+
get
392+
{
393+
return !isSortedAscending;
394+
}
395+
set
396+
{
397+
isSortedAscending = !value;
398+
if (value) SortBy(sortedColumn);
399+
}
400+
}
401+
402+
private void NotifyPropertyChanged(string info)
403+
{
404+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
405+
}
332406
}
333407

334408
public class EmptyFolderTextState : INotifyPropertyChanged
335409
{
336-
337-
338410
public Visibility _isVisible;
339411
public Visibility isVisible
340412
{
@@ -358,6 +430,5 @@ private void NotifyPropertyChanged(string info)
358430
{
359431
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(info));
360432
}
361-
362433
}
363434
}

0 commit comments

Comments
 (0)