diff --git a/src/Avalonia.Controls/SystemDialog.cs b/src/Avalonia.Controls/SystemDialog.cs index 4d217d7459..4a9e745e30 100644 --- a/src/Avalonia.Controls/SystemDialog.cs +++ b/src/Avalonia.Controls/SystemDialog.cs @@ -52,6 +52,11 @@ namespace Avalonia.Controls /// public string? DefaultExtension { get; set; } + /// + /// Gets or sets a value indicating whether to display a warning if the user specifies the name of a file that already exists. + /// + public bool? ShowOverwritePrompt { get; set; } + /// /// Shows the save file dialog. /// diff --git a/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs b/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs index 28d40f13b9..405a248caf 100644 --- a/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs +++ b/src/Avalonia.Dialogs/ManagedFileChooserViewModel.cs @@ -17,6 +17,7 @@ namespace Avalonia.Dialogs private readonly ManagedFileDialogOptions _options; public event Action CancelRequested; public event Action CompleteRequested; + public event Action OverwritePrompt; public AvaloniaList QuickLinks { get; } = new AvaloniaList(); @@ -39,6 +40,7 @@ namespace Avalonia.Dialogs private bool _scheduledSelectionValidation; private bool _alreadyCancelled = false; private string _defaultExtension; + private bool _overwritePrompt; private CompositeDisposable _disposables; public string Location @@ -167,6 +169,7 @@ namespace Avalonia.Dialogs { _savingFile = true; _defaultExtension = sfd.DefaultExtension; + _overwritePrompt = sfd.ShowOverwritePrompt ?? true; FileName = sfd.InitialFileName; } @@ -360,7 +363,16 @@ namespace Avalonia.Dialogs FileName = Path.ChangeExtension(FileName, _defaultExtension); } - CompleteRequested?.Invoke(new[] { Path.Combine(Location, FileName) }); + var fullName = Path.Combine(Location, FileName); + + if (_overwritePrompt && File.Exists(fullName)) + { + OverwritePrompt?.Invoke(fullName); + } + else + { + CompleteRequested?.Invoke(new[] { fullName }); + } } } else diff --git a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs index f9e62d905b..1970c5557d 100644 --- a/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs +++ b/src/Avalonia.Dialogs/ManagedFileDialogExtensions.cs @@ -1,3 +1,4 @@ +using System.IO; using System.Linq; using System.Threading.Tasks; using Avalonia.Controls; @@ -31,6 +32,75 @@ namespace Avalonia.Dialogs dialog.Close(); }; + model.OverwritePrompt += async (filename) => + { + Window overwritePromptDialog = new Window() + { + Title = "Confirm Save As", + SizeToContent = SizeToContent.WidthAndHeight, + WindowStartupLocation = WindowStartupLocation.CenterOwner, + Padding = new Thickness(10), + MinWidth = 270 + }; + + string name = Path.GetFileName(filename); + + var panel = new DockPanel() + { + HorizontalAlignment = Layout.HorizontalAlignment.Stretch + }; + + var label = new Label() + { + Content = $"{name} already exists.\nDo you want to replace it?" + }; + + panel.Children.Add(label); + DockPanel.SetDock(label, Dock.Top); + + var buttonPanel = new StackPanel() + { + HorizontalAlignment = Layout.HorizontalAlignment.Right, + Orientation = Layout.Orientation.Horizontal, + Spacing = 10 + }; + + var button = new Button() + { + Content = "Yes", + HorizontalAlignment = Layout.HorizontalAlignment.Right + }; + + button.Click += (sender, args) => + { + result = new string[1] { filename }; + overwritePromptDialog.Close(); + dialog.Close(); + }; + + buttonPanel.Children.Add(button); + + button = new Button() + { + Content = "No", + HorizontalAlignment = Layout.HorizontalAlignment.Right + }; + + button.Click += (sender, args) => + { + overwritePromptDialog.Close(); + }; + + buttonPanel.Children.Add(button); + + panel.Children.Add(buttonPanel); + DockPanel.SetDock(buttonPanel, Dock.Bottom); + + overwritePromptDialog.Content = panel; + + await overwritePromptDialog.ShowDialog(dialog); + }; + model.CancelRequested += dialog.Close; await dialog.ShowDialog(parent); diff --git a/src/Avalonia.X11/NativeDialogs/Gtk.cs b/src/Avalonia.X11/NativeDialogs/Gtk.cs index 872c824f74..a0f146afa8 100644 --- a/src/Avalonia.X11/NativeDialogs/Gtk.cs +++ b/src/Avalonia.X11/NativeDialogs/Gtk.cs @@ -179,6 +179,9 @@ namespace Avalonia.X11.NativeDialogs [DllImport(GtkName)] public static extern void gtk_file_chooser_set_select_multiple(IntPtr chooser, bool allow); + + [DllImport(GtkName)] + public static extern void gtk_file_chooser_set_do_overwrite_confirmation(IntPtr chooser, bool do_overwrite_confirmation); [DllImport(GtkName)] public static extern void diff --git a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs index 6cf6c6f35f..1a6514eb03 100644 --- a/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs +++ b/src/Avalonia.X11/NativeDialogs/GtkNativeFileDialogs.cs @@ -16,7 +16,7 @@ namespace Avalonia.X11.NativeDialogs { private Task _initialized; private unsafe Task ShowDialog(string title, IWindowImpl parent, GtkFileChooserAction action, - bool multiSelect, string initialFileName, IEnumerable filters, string defaultExtension) + bool multiSelect, string initialFileName, IEnumerable filters, string defaultExtension, bool overwritePrompt) { IntPtr dlg; using (var name = new Utf8Buffer(title)) @@ -109,6 +109,8 @@ namespace Avalonia.X11.NativeDialogs gtk_file_chooser_set_filename(dlg, fn); } + gtk_file_chooser_set_do_overwrite_confirmation(dlg, overwritePrompt); + gtk_window_present(dlg); return tcs.Task; } @@ -148,7 +150,7 @@ namespace Avalonia.X11.NativeDialogs (dialog as OpenFileDialog)?.AllowMultiple ?? false, Path.Combine(string.IsNullOrEmpty(dialog.Directory) ? "" : dialog.Directory, string.IsNullOrEmpty(dialog.InitialFileName) ? "" : dialog.InitialFileName), dialog.Filters, - (dialog as SaveFileDialog)?.DefaultExtension)); + (dialog as SaveFileDialog)?.DefaultExtension, (dialog as SaveFileDialog)?.ShowOverwritePrompt ?? false)); } public async Task ShowFolderDialogAsync(OpenFolderDialog dialog, Window parent) @@ -160,7 +162,7 @@ namespace Avalonia.X11.NativeDialogs return await await RunOnGlibThread(async () => { var res = await ShowDialog(dialog.Title, platformImpl, - GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null); + GtkFileChooserAction.SelectFolder, false, dialog.Directory, null, null, false); return res?.FirstOrDefault(); }); } diff --git a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs index 29844368db..531020698a 100644 --- a/src/Windows/Avalonia.Win32/SystemDialogImpl.cs +++ b/src/Windows/Avalonia.Win32/SystemDialogImpl.cs @@ -40,6 +40,16 @@ namespace Avalonia.Win32 { options |= FILEOPENDIALOGOPTIONS.FOS_ALLOWMULTISELECT; } + + if (dialog is SaveFileDialog saveFileDialog) + { + var overwritePrompt = saveFileDialog.ShowOverwritePrompt ?? true; + + if (!overwritePrompt) + { + options &= ~FILEOPENDIALOGOPTIONS.FOS_OVERWRITEPROMPT; + } + } frm.SetOptions(options); var defaultExtension = (dialog as SaveFileDialog)?.DefaultExtension ?? "";