diff --git a/src/Avalonia.Controls/Platform/IMountedDriveInfoProvider.cs b/src/Avalonia.Controls/Platform/IMountedDriveInfoProvider.cs
deleted file mode 100644
index 18413ff591..0000000000
--- a/src/Avalonia.Controls/Platform/IMountedDriveInfoProvider.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) The Avalonia Project. All rights reserved.
-// Licensed under the MIT license. See licence.md file in the project root for full license information.
-
-using System;
-using System.Collections.ObjectModel;
-using System.Threading.Tasks;
-using Avalonia.Platform;
-
-namespace Avalonia.Controls.Platform
-{
- ///
- /// Defines a platform-specific drive mount information provider implementation.
- ///
- public interface IMountedDriveInfoProvider : IDisposable
- {
- ///
- /// Observable list of currently-mounted drives.
- ///
- ObservableCollection MountedDrives { get; }
- }
-}
diff --git a/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs
new file mode 100644
index 0000000000..1420fce3c2
--- /dev/null
+++ b/src/Avalonia.Controls/Platform/IMountedVolumeInfoProvider.cs
@@ -0,0 +1,23 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using System;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using Avalonia.Platform;
+
+namespace Avalonia.Controls.Platform
+{
+ ///
+ /// Defines a platform-specific mount volumes info provider implementation.
+ ///
+ public interface IMountedVolumeInfoProvider
+ {
+ ///
+ /// Listens to any changes in volume mounts and
+ /// forwards updates to the referenced
+ /// .
+ ///
+ IDisposable Listen(ObservableCollection mountedDrives);
+ }
+}
diff --git a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
index 13b8bc45f5..2fa237c714 100644
--- a/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
+++ b/src/Avalonia.Controls/Platform/MountedDriveInfo.cs
@@ -8,19 +8,19 @@ namespace Avalonia.Controls.Platform
///
/// Describes a Drive's properties.
///
- public class MountedDriveInfo : IEquatable
+ public class MountedVolumeInfo : IEquatable
{
- public string DriveLabel { get; set; }
- public string DriveName { get; set; }
- public ulong DriveSizeBytes { get; set; }
+ public string VolumeLabel { get; set; }
+ public string VolumeName { get; set; }
+ public ulong VolumeSizeBytes { get; set; }
public string DevicePath { get; set; }
public string MountPath { get; set; }
- public bool Equals(MountedDriveInfo other)
+ public bool Equals(MountedVolumeInfo other)
{
- return this.DriveLabel.Equals(other.DriveLabel) &&
- this.DriveName.Equals(other.DriveName) &&
- this.DriveSizeBytes.Equals(other.DriveSizeBytes) &&
+ return this.VolumeLabel.Equals(other.VolumeLabel) &&
+ this.VolumeName.Equals(other.VolumeName) &&
+ this.VolumeSizeBytes.Equals(other.VolumeSizeBytes) &&
this.DevicePath.Equals(other.DevicePath) &&
this.MountPath.Equals(other.MountPath);
}
diff --git a/src/Avalonia.Dialogs/ManagedFileChooserSources.cs b/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
index 7d2698463d..a7723c6757 100644
--- a/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
+++ b/src/Avalonia.Dialogs/ManagedFileChooserSources.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reactive.Linq;
@@ -20,6 +21,7 @@ namespace Avalonia.Dialogs
= DefaultGetAllItems;
public ManagedFileChooserNavigationItem[] GetAllItems() => GetAllItemsDelegate(this);
+ public static readonly ObservableCollection MountedVolumes = new ObservableCollection();
public static ManagedFileChooserNavigationItem[] DefaultGetAllItems(ManagedFileChooserSources sources)
{
@@ -73,11 +75,7 @@ namespace Avalonia.Dialogs
}
else
{
- var drivesInfos = AvaloniaLocator.CurrentMutable
- .GetService()
- .MountedDrives;
-
- return drivesInfos
+ return MountedVolumes
.Where(x => !x.MountPath.StartsWith("/boot"))
.Select(x =>
{
@@ -92,13 +90,13 @@ namespace Avalonia.Dialogs
}
else
{
- var dNameEmpty = string.IsNullOrEmpty(x.DriveLabel.Trim());
+ var dNameEmpty = string.IsNullOrEmpty(x.VolumeLabel.Trim());
return new ManagedFileChooserNavigationItem
{
ItemType = ManagedFileChooserItemType.Volume,
- DisplayName = dNameEmpty ? $"{ByteSizeHelper.ToString(x.DriveSizeBytes)} Volume"
- : x.DriveLabel,
+ DisplayName = dNameEmpty ? $"{ByteSizeHelper.ToString(x.VolumeSizeBytes)} Volume"
+ : x.VolumeLabel,
Path = x.MountPath
};
}
diff --git a/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs b/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs
index 93b9b14535..a6847939d7 100644
--- a/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs
+++ b/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs
@@ -36,7 +36,9 @@ namespace Avalonia.Dialogs
private bool _selectingDirectory;
private bool _savingFile;
private bool _scheduledSelectionValidation;
+ private bool _alreadyCancelled = false;
private string _defaultExtension;
+ private CompositeDisposable _disposables;
public string Location
{
@@ -95,30 +97,36 @@ namespace Avalonia.Dialogs
}
}
- private void RefreshQuickLinks(object _ = null)
+ private void RefreshQuickLinks(ManagedFileChooserSources quickSources)
{
- var quickSources = AvaloniaLocator.Current
- .GetService()
- ?? new ManagedFileChooserSources();
-
QuickLinks.Clear();
QuickLinks.AddRange(quickSources.GetAllItems().Select(i => new ManagedFileChooserItemViewModel(i)));
}
public ManagedFileChooserViewModel(FileSystemDialog dialog)
{
- var drivesInfoSrv = AvaloniaLocator.Current
- .GetService()
- .MountedDrives;
+ _disposables = new CompositeDisposable();
+
+ var quickSources = AvaloniaLocator.Current
+ .GetService()
+ ?? new ManagedFileChooserSources();
+
+ var sub1 = AvaloniaLocator.Current
+ .GetService()
+ .Listen(ManagedFileChooserSources.MountedVolumes);
- var sub1 = Observable.FromEventPattern(drivesInfoSrv, nameof(drivesInfoSrv.CollectionChanged))
+ var sub2 = Observable.FromEventPattern(ManagedFileChooserSources.MountedVolumes,
+ nameof(ManagedFileChooserSources.MountedVolumes.CollectionChanged))
.ObserveOn(AvaloniaScheduler.Instance)
- .Subscribe(RefreshQuickLinks);
+ .Subscribe(x => RefreshQuickLinks(quickSources));
- CompleteRequested += delegate { sub1?.Dispose(); };
- CancelRequested += delegate { sub1?.Dispose(); };
+ _disposables.Add(sub1);
+ _disposables.Add(sub2);
- RefreshQuickLinks();
+ CompleteRequested += delegate { _disposables?.Dispose(); };
+ CancelRequested += delegate { _disposables?.Dispose(); };
+
+ RefreshQuickLinks(quickSources);
Title = dialog.Title ?? (
dialog is OpenFileDialog ? "Open file"
@@ -318,7 +326,14 @@ namespace Avalonia.Dialogs
public void Cancel()
{
- CancelRequested?.Invoke();
+ if (!_alreadyCancelled)
+ {
+ // INFO: Don't misplace this check or it might cause
+ // StackOverflowException because of recursive
+ // event invokes.
+ _alreadyCancelled = true;
+ CancelRequested?.Invoke();
+ }
}
public void Ok()
diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
index 3068b2ca91..771d2b1b5e 100644
--- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
+++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs
@@ -23,12 +23,16 @@ namespace Avalonia.Dialogs
DataContext = model
};
+ dialog.Closed += delegate { model.Cancel(); };
+
string[] result = null;
+
model.CompleteRequested += items =>
{
result = items;
dialog.Close();
};
+
model.CancelRequested += dialog.Close;
await dialog.ShowDialog