using System; using System.Buffers; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Security; using System.Text; using System.Threading.Tasks; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Presenters; using Avalonia.Dialogs; using Avalonia.Layout; using Avalonia.Markup.Xaml; using Avalonia.Platform.Storage; using Avalonia.Platform.Storage.FileIO; #pragma warning disable CS0618 // Type or member is obsolete #nullable enable namespace ControlCatalog.Pages { public partial class DialogsPage : UserControl { public DialogsPage() { InitializeComponent(); IStorageFolder? lastSelectedDirectory = null; IStorageItem? lastSelectedItem = null; bool ignoreTextChanged = false; var results = PickerLastResults; var resultsVisible = PickerLastResultsVisible; var bookmarkContainer = BookmarkContainer; var openedFileContent = OpenedFileContent; var openMultiple = OpenMultiple; var currentFolderBox = CurrentFolderBox; currentFolderBox.TextChanged += async (sender, args) => { if (ignoreTextChanged) return; if (Enum.TryParse(currentFolderBox.Text, true, out var folderEnum)) { lastSelectedDirectory = await GetStorageProvider().TryGetWellKnownFolderAsync(folderEnum); } else if (!string.IsNullOrWhiteSpace(currentFolderBox.Text)) { if (!Uri.TryCreate(currentFolderBox.Text, UriKind.Absolute, out var folderLink)) { Uri.TryCreate("file://" + currentFolderBox.Text, UriKind.Absolute, out folderLink); } if (folderLink is not null) { try { lastSelectedDirectory = await GetStorageProvider().TryGetFolderFromPathAsync(folderLink); } catch (SecurityException) { } } } }; List GetFilters() { return GetFileTypes()?.Select(f => new FileDialogFilter { Name = f.Name, Extensions = f.Patterns!.ToList() }).ToList() ?? new List(); } List? GetFileTypes() { var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content ?? "None"; var binLogType = new FilePickerFileType("Binary Log") { Patterns = new[] { "*.binlog", "*.buildlog" }, MimeTypes = new[] { "application/binlog", "application/buildlog" }, AppleUniformTypeIdentifiers = new[] { "public.data" } }; return selectedItem switch { "All + TXT + BinLog" => new List { FilePickerFileTypes.All, FilePickerFileTypes.TextPlain, binLogType }, "Binlog" => new List { binLogType }, "TXT extension only" => new List { new("TXT") { Patterns = FilePickerFileTypes.TextPlain.Patterns } }, "TXT mime only" => new List { new("TXT") { MimeTypes = FilePickerFileTypes.TextPlain.MimeTypes } }, "TXT apple type id only" => new List { new("TXT") { AppleUniformTypeIdentifiers = FilePickerFileTypes.TextPlain.AppleUniformTypeIdentifiers } }, _ => null }; } OpenFile.Click += async delegate { // Almost guaranteed to exist var uri = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName; var initialFileName = uri == null ? null : System.IO.Path.GetFileName(uri); var initialDirectory = uri == null ? null : System.IO.Path.GetDirectoryName(uri); var result = await new OpenFileDialog() { Title = "Open file", Filters = GetFilters(), Directory = initialDirectory, InitialFileName = initialFileName }.ShowAsync(GetWindow()); results.ItemsSource = result; resultsVisible.IsVisible = result?.Any() == true; }; OpenMultipleFiles.Click += async delegate { var result = await new OpenFileDialog() { Title = "Open multiple files", Filters = GetFilters(), Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null, AllowMultiple = true }.ShowAsync(GetWindow()); results.ItemsSource = result; resultsVisible.IsVisible = result?.Any() == true; }; SaveFile.Click += async delegate { var filters = GetFilters(); var result = await new SaveFileDialog() { Title = "Save file", Filters = filters, Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null, DefaultExtension = filters?.Any() == true ? "txt" : null, InitialFileName = "test.txt" }.ShowAsync(GetWindow()); results.ItemsSource = new[] { result }; resultsVisible.IsVisible = result != null; }; SelectFolder.Click += async delegate { var result = await new OpenFolderDialog() { Title = "Select folder", Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null, }.ShowAsync(GetWindow()); if (string.IsNullOrEmpty(result)) { resultsVisible.IsVisible = false; } else { SetFolder(await GetStorageProvider().TryGetFolderFromPathAsync(result!)); results.ItemsSource = new[] { result }; resultsVisible.IsVisible = true; } }; OpenBoth.Click += async delegate { var result = await new OpenFileDialog() { Title = "Select both", Directory = lastSelectedDirectory?.Path is {IsAbsoluteUri:true} path ? path.LocalPath : null, AllowMultiple = true }.ShowManagedAsync(GetWindow(), new ManagedFileDialogOptions { AllowDirectorySelection = true }); results.ItemsSource = result; resultsVisible.IsVisible = result?.Any() == true; }; DecoratedWindow.Click += delegate { new DecoratedWindow().Show(); }; DecoratedWindowDialog.Click += delegate { _ = new DecoratedWindow().ShowDialog(GetWindow()); }; Dialog.Click += delegate { var window = CreateSampleWindow(); window.Height = 200; _ = window.ShowDialog(GetWindow()); }; DialogNoTaskbar.Click += delegate { var window = CreateSampleWindow(); window.Height = 200; window.ShowInTaskbar = false; _ = window.ShowDialog(GetWindow()); }; OwnedWindow.Click += delegate { var window = CreateSampleWindow(); window.Show(GetWindow()); }; OwnedWindowNoTaskbar.Click += delegate { var window = CreateSampleWindow(); window.ShowInTaskbar = false; window.Show(GetWindow()); }; OpenFilePicker.Click += async delegate { var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions() { Title = "Open file", FileTypeFilter = GetFileTypes(), SuggestedFileName = "FileName", SuggestedStartLocation = lastSelectedDirectory, AllowMultiple = openMultiple.IsChecked == true }); await SetPickerResult(result); }; SaveFilePicker.Click += async delegate { var fileTypes = GetFileTypes(); var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions() { Title = "Save file", FileTypeChoices = fileTypes, SuggestedStartLocation = lastSelectedDirectory, SuggestedFileName = "FileName", ShowOverwritePrompt = true }); if (file is not null) { try { // Sync disposal of StreamWriter is not supported on WASM #if NET6_0_OR_GREATER await using var stream = await file.OpenWriteAsync(); await using var writer = new System.IO.StreamWriter(stream); #else using var stream = await file.OpenWriteAsync(); using var writer = new System.IO.StreamWriter(stream); #endif await writer.WriteLineAsync(openedFileContent.Text); SetFolder(await file.GetParentAsync()); } catch (Exception ex) { openedFileContent.Text = ex.ToString(); } } await SetPickerResult(file is null ? null : new[] { file }); }; SaveFilePickerWithResult.Click += async delegate { var result = await GetStorageProvider().SaveFilePickerWithResultAsync(new FilePickerSaveOptions() { Title = "Save file", FileTypeChoices = [FilePickerFileTypes.Json, FilePickerFileTypes.Xml], SuggestedStartLocation = lastSelectedDirectory, SuggestedFileName = "FileName", ShowOverwritePrompt = true }); try { if (result.File is { } file) { // Sync disposal of StreamWriter is not supported on WASM #if NET6_0_OR_GREATER await using var stream = await file.OpenWriteAsync(); await using var writer = new System.IO.StreamWriter(stream); #else using var stream = await file.OpenWriteAsync(); using var writer = new System.IO.StreamWriter(stream); #endif if (result.SelectedFileType == FilePickerFileTypes.Xml) { await writer.WriteLineAsync("Test"); } else { await writer.WriteLineAsync("""{ "sample": "Test" }"""); } SetFolder(await result.File.GetParentAsync()); } } catch (Exception ex) { openedFileContent.Text = ex.ToString(); } await SetPickerResult(result.File is null ? null : new[] { result.File }, result.SelectedFileType); }; OpenFolderPicker.Click += async delegate { var folders = await GetStorageProvider().OpenFolderPickerAsync(new FolderPickerOpenOptions() { Title = "Folder file", SuggestedStartLocation = lastSelectedDirectory, SuggestedFileName = "FileName", AllowMultiple = openMultiple.IsChecked == true }); await SetPickerResult(folders); }; OpenFileFromBookmark.Click += async delegate { var file = bookmarkContainer.Text is not null ? await GetStorageProvider().OpenFileBookmarkAsync(bookmarkContainer.Text) : null; await SetPickerResult(file is null ? null : new[] { file }); }; OpenFolderFromBookmark.Click += async delegate { var folder = bookmarkContainer.Text is not null ? await GetStorageProvider().OpenFolderBookmarkAsync(bookmarkContainer.Text) : null; await SetPickerResult(folder is null ? null : new[] { folder }); }; LaunchUri.Click += async delegate { var statusBlock = LaunchStatus; if (Uri.TryCreate(UriToLaunch.Text, UriKind.Absolute, out var uri)) { var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchUriAsync(uri); statusBlock.Text = "LaunchUriAsync returned " + result; } else { statusBlock.Text = "Can't parse the Uri"; } }; LaunchFile.Click += async delegate { var statusBlock = LaunchStatus; if (lastSelectedItem is not null) { var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchFileAsync(lastSelectedItem); statusBlock.Text = "LaunchFileAsync returned " + result; } else { statusBlock.Text = "Please select any file or folder first"; } }; void SetFolder(IStorageFolder? folder) { ignoreTextChanged = true; lastSelectedDirectory = folder; lastSelectedItem = folder; currentFolderBox.Text = folder?.Path is { IsAbsoluteUri: true } abs ? abs.LocalPath : folder?.Path?.ToString(); ignoreTextChanged = false; } async Task SetPickerResult(IReadOnlyCollection? items, FilePickerFileType? selectedType = null) { items ??= Array.Empty(); bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmarkAsync() : "Can't bookmark"; var mappedResults = new List(); string resultText = ""; if (items.FirstOrDefault() is IStorageItem item) { resultText += item is IStorageFile ? "File:" : "Folder:"; resultText += Environment.NewLine; var props = await item.GetBasicPropertiesAsync(); resultText += @$"Size: {props.Size} DateCreated: {props.DateCreated} DateModified: {props.DateModified} CanBookmark: {item.CanBookmark} "; if (item is IStorageFile file) { resultText += @$" Content: "; try { resultText += await ReadTextFromFile(file, 500); } catch (Exception ex) { resultText += ex.ToString(); } } if (item is IStorageFolder storageFolder) { SetFolder(storageFolder); } else { var parent = await item.GetParentAsync(); SetFolder(parent); if (parent is not null) { mappedResults.Add(FullPathOrName(parent)); } } foreach (var selectedItem in items) { mappedResults.Add("+> " + FullPathOrName(selectedItem)); if (selectedItem is IStorageFolder folder) { await foreach (var innerItem in folder.GetItemsAsync()) { mappedResults.Add("++> " + FullPathOrName(innerItem)); } } } lastSelectedItem = item; } if (selectedType is not null) { resultText += Environment.NewLine + "Selected type: " + selectedType.Name; } openedFileContent.Text = resultText; results.ItemsSource = mappedResults; resultsVisible.IsVisible = mappedResults.Any(); } } internal static async Task ReadTextFromFile(IStorageFile file, int length) { #if NET6_0_OR_GREATER await using var stream = await file.OpenReadAsync(); #else using var stream = await file.OpenReadAsync(); #endif using var reader = new System.IO.StreamReader(stream); // 4GB file test, shouldn't load more than 10000 chars into a memory. var buffer = ArrayPool.Shared.Rent(length); try { var charsRead = await reader.ReadAsync(buffer, 0, length); return new string(buffer, 0, charsRead); } finally { ArrayPool.Shared.Return(buffer); } } protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e) { base.OnAttachedToVisualTree(e); var openedFileContent = OpenedFileContent; try { var storageProvider = GetStorageProvider(); openedFileContent.Text = $@"CanOpen: {storageProvider.CanOpen} CanSave: {storageProvider.CanSave} CanPickFolder: {storageProvider.CanPickFolder}"; } catch (Exception ex) { openedFileContent.Text = "Storage provider is not available: " + ex.Message; } } private Window CreateSampleWindow() { Button button; Button dialogButton; var window = new Window { Height = 200, Width = 200, Content = new StackPanel { Spacing = 4, Children = { new TextBlock { Text = "Hello world!" }, (button = new Button { HorizontalAlignment = HorizontalAlignment.Center, Content = "Click to close", IsDefault = true }), (dialogButton = new Button { HorizontalAlignment = HorizontalAlignment.Center, Content = "Dialog", IsDefault = false }) } }, WindowStartupLocation = WindowStartupLocation.CenterOwner }; button.Click += (_, __) => window.Close(); dialogButton.Click += (_, __) => { var dialog = CreateSampleWindow(); dialog.Height = 200; dialog.ShowDialog(window); }; return window; } private IStorageProvider GetStorageProvider() { var forceManaged = ForceManaged.IsChecked ?? false; return forceManaged ? new ManagedStorageProvider(GetWindow()) // NOTE: In your production App use 'AppBuilder.UseManagedSystemDialogs()' : GetTopLevel().StorageProvider; } private static string FullPathOrName(IStorageItem? item) { if (item is null) return "(null)"; return item.Path is { IsAbsoluteUri: true } path ? path.ToString() : item.Name; } Window GetWindow() => TopLevel.GetTopLevel(this) as Window ?? throw new NullReferenceException("Invalid Owner"); TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner"); } #pragma warning restore CS0618 // Type or member is obsolete }