19 changed files with 765 additions and 21 deletions
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<IsPackable>false</IsPackable> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="Internal\ManagedFileChooser.xaml"> |
|||
<SubType>Designer</SubType> |
|||
</EmbeddedResource> |
|||
<EmbeddedResource Include="Internal\ManagedFileDialog.xaml"> |
|||
<SubType>Designer</SubType> |
|||
</EmbeddedResource> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" /> |
|||
<ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" /> |
|||
</ItemGroup> |
|||
</Project> |
|||
@ -0,0 +1,20 @@ |
|||
using Avalonia.Controls; |
|||
using Avalonia.Layout; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ChildFitter : Decorator |
|||
{ |
|||
protected override Size MeasureOverride(Size availableSize) |
|||
{ |
|||
return new Size(0, 0); |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
Child.Measure(finalSize); |
|||
base.ArrangeOverride(finalSize); |
|||
return finalSize; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
using System.Collections.Generic; |
|||
using System.ComponentModel; |
|||
using System.Runtime.CompilerServices; |
|||
using JetBrains.Annotations; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class InternalViewModelBase : INotifyPropertyChanged |
|||
{ |
|||
public event PropertyChangedEventHandler PropertyChanged; |
|||
|
|||
[NotifyPropertyChangedInvocator] |
|||
protected bool RaiseAndSetIfChanged<T>(ref T field, T value, [CallerMemberName] string propertyName = null) |
|||
{ |
|||
if (!EqualityComparer<T>.Default.Equals(field, value)) |
|||
{ |
|||
field = value; |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
[NotifyPropertyChangedInvocator] |
|||
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) |
|||
{ |
|||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,74 @@ |
|||
<UserControl xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
|||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
|||
xmlns:dialogs="clr-namespace:Avalonia.Dialogs.Internal" |
|||
xmlns:internal="clr-namespace:Avalonia.Dialogs.Internal"> |
|||
<UserControl.Resources> |
|||
<DrawingGroup x:Key="LevelUp"> |
|||
<GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" /> |
|||
<GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M14.5,0L6.39,0 5.39,2 2.504,2C1.677,2,1,2.673,1,3.5L1,10.582 1,10.586 1,15.414 3,13.414 3,16 7,16 7,13.414 9,15.414 9,13 14.5,13C15.327,13,16,12.327,16,11.5L16,1.5C16,0.673,15.327,0,14.5,0" /> |
|||
<GeometryDrawing Brush="#FFDCB679" Geometry="F1M14,3L7.508,3 8.008,2 8.012,2 14,2z M14.5,1L7.008,1 6.008,3 2.504,3C2.227,3,2,3.224,2,3.5L2,9.582 4.998,6.586 9,10.586 9,12 14.5,12C14.775,12,15,11.776,15,11.5L15,1.5C15,1.224,14.775,1,14.5,1" /> |
|||
<GeometryDrawing Brush="#FF00529C" Geometry="F1M8,11L5,8 2,11 2,13 4,11 4,15 6,15 6,11 8,13z" /> |
|||
<GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M8.0001,1.9996L7.5001,3.0006 14.0001,3.0006 14.0001,1.9996z" /> |
|||
</DrawingGroup> |
|||
<dialogs:ResourceSelectorConverter x:Key="Icons"> |
|||
<DrawingGroup x:Key="Icon_Folder"><GeometryDrawing Brush="#00FFFFFF" Geometry="F1M0,0L16,0 16,16 0,16z" /><GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M1.5,1L9.61,1 10.61,3 13.496,3C14.323,3,14.996,3.673,14.996,4.5L14.996,12.5C14.996,13.327,14.323,14,13.496,14L1.5,14C0.673,14,0,13.327,0,12.5L0,2.5C0,1.673,0.673,1,1.5,1" /><GeometryDrawing Brush="#FFDCB67A" Geometry="F1M2,3L8.374,3 8.874,4 2,4z M13.496,4L10,4 9.992,4 8.992,2 1.5,2C1.225,2,1,2.224,1,2.5L1,12.5C1,12.776,1.225,13,1.5,13L13.496,13C13.773,13,13.996,12.776,13.996,12.5L13.996,4.5C13.996,4.224,13.773,4,13.496,4" /><GeometryDrawing Brush="#FFEFEFF0" Geometry="F1M2,3L8.374,3 8.874,4 2,4z" /></DrawingGroup> |
|||
<DrawingGroup x:Key="Icon_File"><GeometryDrawing Brush="#00FFFFFF" Geometry="F1M16,16L0,16 0,0 16,0z" /><GeometryDrawing Brush="#FFF6F6F6" Geometry="F1M4,15C3.03,15,2,14.299,2,13L2,3C2,1.701,3.03,1,4,1L10.061,1 14,4.556 14,13C14,13.97,13.299,15,12,15z" /><GeometryDrawing Brush="#FF9B4E96" Geometry="F1M12,13L4,13 4,3 9,3 9,6 12,6z M9.641,2L3.964,2C3.964,2,3,2,3,3L3,13C3,14,3.964,14,3.964,14L11.965,14C12.965,14,13,13,13,13L13,5z" /><GeometryDrawing Brush="#FFF0EFF1" Geometry="F1M4,3L9,3 9,6 12,6 12,13 4,13z" /></DrawingGroup> |
|||
</dialogs:ResourceSelectorConverter> |
|||
</UserControl.Resources> |
|||
<UserControl.DataTemplates> |
|||
<DataTemplate DataType="internal:ManagedFileChooserItemViewModel"> |
|||
<StackPanel Background="Transparent" Orientation="Horizontal" |
|||
Cursor="Hand"> |
|||
|
|||
<DrawingPresenter Width="16" Height="16" Drawing="{Binding IconKey, Converter={StaticResource Icons}}"/> |
|||
<TextBlock Text="{Binding DisplayName}"/> |
|||
</StackPanel> |
|||
</DataTemplate> |
|||
</UserControl.DataTemplates> |
|||
<DockPanel> |
|||
<DockPanel DockPanel.Dock="Top" Margin="5"> |
|||
|
|||
<dialogs:ChildFitter DockPanel.Dock="Right" Width="{Binding ElementName=Location, Path=Bounds.Height}"> |
|||
<Button Command="{Binding GoUp}" > |
|||
|
|||
<DrawingPresenter Drawing="{StaticResource LevelUp}" Stretch="Fill"/> |
|||
|
|||
</Button> |
|||
</dialogs:ChildFitter> |
|||
<TextBox x:Name="Location" IsReadOnly="True" Text="{Binding Location}" Margin="0 0 5 0"/> |
|||
</DockPanel> |
|||
<DockPanel DockPanel.Dock="Bottom"> |
|||
<StackPanel Orientation="Horizontal" DockPanel.Dock="Left"> |
|||
<CheckBox IsChecked="{Binding ShowHiddenFiles}"> |
|||
<TextBlock>Show hidden files</TextBlock> |
|||
</CheckBox> |
|||
</StackPanel> |
|||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> |
|||
<StackPanel.Styles> |
|||
<Style Selector="Button"> |
|||
<Setter Property="Margin">4</Setter> |
|||
</Style> |
|||
</StackPanel.Styles> |
|||
<Button Command="{Binding Ok}">OK</Button> |
|||
<Button Command="{Binding Cancel}">Cancel</Button> |
|||
</StackPanel> |
|||
</DockPanel> |
|||
<DropDown DockPanel.Dock="Bottom" |
|||
IsVisible="{Binding ShowFilters}" |
|||
Items="{Binding Filters}" |
|||
SelectedItem="{Binding SelectedFilter}" |
|||
/> |
|||
<ListBox Margin="5" BorderBrush="Transparent" x:Name="QuickLinks" Items="{Binding QuickLinks}" |
|||
SelectedIndex="{Binding QuickLinksSelectedIndex}" |
|||
DockPanel.Dock="Left" Background="{DynamicResource ThemeControlMidBrush}" Focusable="False" /> |
|||
<ListBox x:Name="Files" |
|||
VirtualizationMode="Simple" |
|||
Items="{Binding Items}" |
|||
Margin="5" |
|||
SelectionMode="{Binding SelectionMode}" |
|||
SelectedItems="{Binding SelectedItems}"/> |
|||
</DockPanel> |
|||
|
|||
</UserControl> |
|||
@ -0,0 +1,65 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Input; |
|||
using Avalonia.Interactivity; |
|||
using Avalonia.LogicalTree; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ManagedFileChooser : UserControl |
|||
{ |
|||
private Control _quickLinksRoot; |
|||
private ListBox _filesView; |
|||
|
|||
public ManagedFileChooser() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
AddHandler(PointerPressedEvent, OnPointerPressed, RoutingStrategies.Tunnel); |
|||
_quickLinksRoot = this.FindControl<Control>("QuickLinks"); |
|||
_filesView = this.FindControl<ListBox>("Files"); |
|||
} |
|||
|
|||
ManagedFileChooserViewModel Model => DataContext as ManagedFileChooserViewModel; |
|||
|
|||
private void OnPointerPressed(object sender, PointerPressedEventArgs e) |
|||
{ |
|||
var model = (e.Source as StyledElement)?.DataContext as ManagedFileChooserItemViewModel; |
|||
if(model == null) |
|||
return; |
|||
|
|||
var isQuickLink = _quickLinksRoot.IsLogicalParentOf(e.Source as Control); |
|||
if (e.ClickCount == 2 || isQuickLink) |
|||
{ |
|||
if (model.IsDirectory) |
|||
Model?.Navigate(model.Path); |
|||
else |
|||
Model?.SelectSingleFile(model); |
|||
e.Handled = true; |
|||
} |
|||
} |
|||
|
|||
protected override async void OnDataContextChanged(EventArgs e) |
|||
{ |
|||
base.OnDataContextChanged(e); |
|||
var model = (DataContext as ManagedFileChooserViewModel); |
|||
if (model == null) |
|||
return; |
|||
var preselected = model.SelectedItems.FirstOrDefault(); |
|||
if(preselected == null) |
|||
return; |
|||
|
|||
//Let everything to settle down and scroll to selected item
|
|||
await Task.Delay(100); |
|||
if (preselected != model.SelectedItems.FirstOrDefault()) |
|||
return; |
|||
|
|||
// Workaround for ListBox bug, scroll to the previous file
|
|||
var indexOfPreselected = model.Items.IndexOf(preselected); |
|||
if (indexOfPreselected > 1) |
|||
_filesView.ScrollIntoView(model.Items[indexOfPreselected - 1]); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Controls; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ManagedFileChooserFilterViewModel : InternalViewModelBase |
|||
{ |
|||
private readonly string[] _extensions; |
|||
public string Name { get; } |
|||
|
|||
public ManagedFileChooserFilterViewModel(FileDialogFilter filter) |
|||
{ |
|||
Name = filter.Name; |
|||
if (filter.Extensions.Contains("*")) |
|||
return; |
|||
_extensions = filter.Extensions?.Select(e => "." + e.ToLowerInvariant()).ToArray(); |
|||
} |
|||
|
|||
public ManagedFileChooserFilterViewModel() |
|||
{ |
|||
Name = "All files"; |
|||
} |
|||
|
|||
public bool Match(string filename) |
|||
{ |
|||
if (_extensions == null) |
|||
return true; |
|||
foreach(var ext in _extensions) |
|||
if (filename.EndsWith(ext, StringComparison.InvariantCultureIgnoreCase)) |
|||
return true; |
|||
return false; |
|||
} |
|||
|
|||
public override string ToString() => Name; |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ManagedFileChooserItemViewModel : InternalViewModelBase |
|||
{ |
|||
private string _displayName; |
|||
private string _path; |
|||
private bool _isDirectory; |
|||
|
|||
public string DisplayName |
|||
{ |
|||
get => _displayName; |
|||
set => RaiseAndSetIfChanged(ref _displayName, value); |
|||
} |
|||
|
|||
public string Path |
|||
{ |
|||
get => _path; |
|||
set => RaiseAndSetIfChanged(ref _path, value); |
|||
} |
|||
|
|||
public string IconKey => IsDirectory ? "Icon_Folder" : "Icon_File"; |
|||
|
|||
public bool IsDirectory |
|||
{ |
|||
get => _isDirectory; |
|||
set |
|||
{ |
|||
if (RaiseAndSetIfChanged(ref _isDirectory, value)) |
|||
RaisePropertyChanged(nameof(IconKey)); |
|||
} |
|||
} |
|||
|
|||
public ManagedFileChooserItemViewModel() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public ManagedFileChooserItemViewModel(ManagedFileChooserNavigationItem item) |
|||
{ |
|||
IsDirectory = true; |
|||
Path = item.Path; |
|||
DisplayName = item.DisplayName; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
using System; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
public class ManagedFileChooserSources |
|||
{ |
|||
public Func<ManagedFileChooserNavigationItem[]> GetUserDirectories { get; set; } |
|||
= DefaultGetUserDirectories; |
|||
|
|||
public Func<ManagedFileChooserNavigationItem[]> GetFileSystemRoots { get; set; } |
|||
= DefaultGetFileSystemRoots; |
|||
|
|||
public Func<ManagedFileChooserSources, ManagedFileChooserNavigationItem[]> GetAllItemsDelegate { get; set; } |
|||
= DefaultGetAllItems; |
|||
|
|||
public ManagedFileChooserNavigationItem[] GetAllItems() => GetAllItemsDelegate(this); |
|||
|
|||
public static ManagedFileChooserNavigationItem[] DefaultGetAllItems(ManagedFileChooserSources sources) |
|||
{ |
|||
return sources.GetUserDirectories().Concat(sources.GetFileSystemRoots()).ToArray(); |
|||
} |
|||
|
|||
private static Environment.SpecialFolder[] s_folders = new[] |
|||
{ |
|||
Environment.SpecialFolder.Desktop, |
|||
Environment.SpecialFolder.UserProfile, |
|||
Environment.SpecialFolder.MyDocuments, |
|||
Environment.SpecialFolder.MyMusic, |
|||
Environment.SpecialFolder.MyPictures, |
|||
Environment.SpecialFolder.MyVideos |
|||
}; |
|||
|
|||
public static ManagedFileChooserNavigationItem[] DefaultGetUserDirectories() |
|||
{ |
|||
return s_folders.Select(Environment.GetFolderPath).Distinct() |
|||
.Where(d => !string.IsNullOrWhiteSpace(d)) |
|||
.Where(Directory.Exists) |
|||
.Select(d => new ManagedFileChooserNavigationItem |
|||
{ |
|||
Path = d, |
|||
DisplayName = Path.GetFileName(d) |
|||
}).ToArray(); |
|||
} |
|||
|
|||
public static ManagedFileChooserNavigationItem[] DefaultGetFileSystemRoots() |
|||
{ |
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
{ |
|||
return DriveInfo.GetDrives().Select(d => new ManagedFileChooserNavigationItem |
|||
{ |
|||
DisplayName = d.Name, |
|||
Path = d.RootDirectory.FullName |
|||
}).ToArray(); |
|||
} |
|||
|
|||
return new[] |
|||
{ |
|||
new ManagedFileChooserNavigationItem |
|||
{ |
|||
DisplayName = "File System", |
|||
Path = "/" |
|||
} |
|||
}; |
|||
} |
|||
} |
|||
|
|||
public class ManagedFileChooserNavigationItem |
|||
{ |
|||
public string DisplayName { get; set; } |
|||
public string Path { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,223 @@ |
|||
using System; |
|||
using System.Collections.Specialized; |
|||
using System.IO; |
|||
using System.Linq; |
|||
using System.Runtime.InteropServices; |
|||
using Avalonia.Collections; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Threading; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ManagedFileChooserViewModel : InternalViewModelBase |
|||
{ |
|||
public event Action CancelRequested; |
|||
public event Action<string[]> CompleteRequested; |
|||
|
|||
public AvaloniaList<ManagedFileChooserItemViewModel> QuickLinks { get; } = |
|||
new AvaloniaList<ManagedFileChooserItemViewModel>(); |
|||
|
|||
public AvaloniaList<ManagedFileChooserItemViewModel> Items { get; } = |
|||
new AvaloniaList<ManagedFileChooserItemViewModel>(); |
|||
|
|||
public AvaloniaList<ManagedFileChooserFilterViewModel> Filters { get; } = |
|||
new AvaloniaList<ManagedFileChooserFilterViewModel>(); |
|||
|
|||
public AvaloniaList<ManagedFileChooserItemViewModel> SelectedItems { get; } = |
|||
new AvaloniaList<ManagedFileChooserItemViewModel>(); |
|||
|
|||
string _location; |
|||
private bool _showHiddenFiles; |
|||
private ManagedFileChooserFilterViewModel _selectedFilter; |
|||
private bool _selectingDirectory; |
|||
private bool _scheduledSelectionValidation; |
|||
|
|||
public string Location |
|||
{ |
|||
get => _location; |
|||
private set => RaiseAndSetIfChanged(ref _location, value); |
|||
} |
|||
|
|||
public bool ShowFilters { get; } |
|||
public SelectionMode SelectionMode { get; } |
|||
public string Title { get; } |
|||
|
|||
public int QuickLinksSelectedIndex |
|||
{ |
|||
get |
|||
{ |
|||
for (var index = 0; index < QuickLinks.Count; index++) |
|||
{ |
|||
var i = QuickLinks[index]; |
|||
if (i.Path == Location) |
|||
return index; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
set => RaisePropertyChanged(nameof(QuickLinksSelectedIndex)); |
|||
} |
|||
|
|||
public ManagedFileChooserFilterViewModel SelectedFilter |
|||
{ |
|||
get => _selectedFilter; |
|||
set |
|||
{ |
|||
RaiseAndSetIfChanged(ref _selectedFilter, value); |
|||
Refresh(); |
|||
} |
|||
} |
|||
|
|||
public bool ShowHiddenFiles |
|||
{ |
|||
get => _showHiddenFiles; |
|||
set |
|||
{ |
|||
RaiseAndSetIfChanged(ref _showHiddenFiles, value); |
|||
Refresh(); |
|||
} |
|||
} |
|||
|
|||
public ManagedFileChooserViewModel(FileSystemDialog dialog) |
|||
{ |
|||
var quickSources = AvaloniaLocator.Current.GetService<ManagedFileChooserSources>() |
|||
?? new ManagedFileChooserSources(); |
|||
QuickLinks.Clear(); |
|||
|
|||
QuickLinks.AddRange(quickSources.GetAllItems().Select(i => new ManagedFileChooserItemViewModel(i))); |
|||
Title = dialog.Title ?? ( |
|||
dialog is OpenFileDialog ? "Open file" |
|||
: dialog is SaveFileDialog ? "Save file" |
|||
: dialog is OpenFolderDialog ? "Select directory" |
|||
: throw new ArgumentException(nameof(dialog))); |
|||
|
|||
var directory = dialog.Directory; |
|||
if (directory == null || !Directory.Exists(directory)) |
|||
directory = Directory.GetCurrentDirectory(); |
|||
|
|||
if (dialog is FileDialog fd) |
|||
{ |
|||
if (fd.Filters?.Count > 0) |
|||
{ |
|||
Filters.AddRange(fd.Filters.Select(f => new ManagedFileChooserFilterViewModel(f))); |
|||
_selectedFilter = Filters[0]; |
|||
ShowFilters = true; |
|||
} |
|||
|
|||
if (dialog is OpenFileDialog ofd) |
|||
{ |
|||
if (ofd.AllowMultiple) |
|||
SelectionMode = SelectionMode.Multiple; |
|||
} |
|||
} |
|||
|
|||
_selectingDirectory = dialog is OpenFolderDialog; |
|||
|
|||
Navigate(directory, (dialog as FileDialog)?.InitialFileName); |
|||
SelectedItems.CollectionChanged += OnSelectionChanged; |
|||
} |
|||
|
|||
private async void OnSelectionChanged(object sender, NotifyCollectionChangedEventArgs e) |
|||
{ |
|||
if(_scheduledSelectionValidation) |
|||
return; |
|||
_scheduledSelectionValidation = true; |
|||
await Dispatcher.UIThread.InvokeAsync(() => |
|||
{ |
|||
try |
|||
{ |
|||
if(_selectingDirectory) |
|||
SelectedItems.Clear(); |
|||
else |
|||
{ |
|||
var invalidItems = SelectedItems.Where(i => i.IsDirectory).ToList(); |
|||
foreach (var item in invalidItems) |
|||
SelectedItems.Remove(item); |
|||
} |
|||
} |
|||
finally |
|||
{ |
|||
_scheduledSelectionValidation = false; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
void NavigateRoot(string initialSelectionName) |
|||
{ |
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
Navigate(Path.GetPathRoot(Environment.GetFolderPath(Environment.SpecialFolder.System)), initialSelectionName); |
|||
else |
|||
Navigate("/", initialSelectionName); |
|||
} |
|||
|
|||
public void Refresh() => Navigate(Location); |
|||
|
|||
public void Navigate(string path, string initialSelectionName = null) |
|||
{ |
|||
if (!Directory.Exists(path)) |
|||
NavigateRoot(initialSelectionName); |
|||
else |
|||
{ |
|||
Location = path; |
|||
Items.Clear(); |
|||
SelectedItems.Clear(); |
|||
var infos = new DirectoryInfo(path).EnumerateFileSystemInfos(); |
|||
if (!ShowHiddenFiles) |
|||
{ |
|||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) |
|||
infos = infos.Where(i => (i.Attributes & (FileAttributes.Hidden | FileAttributes.System)) != 0); |
|||
else |
|||
infos = infos.Where(i => !i.Name.StartsWith(".")); |
|||
} |
|||
|
|||
if (SelectedFilter != null) |
|||
infos = infos.Where(i => i is DirectoryInfo || SelectedFilter.Match(i.Name)); |
|||
|
|||
Items.AddRange(infos.Select(info => new ManagedFileChooserItemViewModel |
|||
{ |
|||
DisplayName = info.Name, |
|||
Path = info.FullName, |
|||
IsDirectory = info is DirectoryInfo |
|||
}).OrderByDescending(x => x.IsDirectory) |
|||
.ThenBy(x => x.DisplayName, StringComparer.InvariantCultureIgnoreCase)); |
|||
|
|||
if (initialSelectionName != null) |
|||
{ |
|||
var sel = Items.FirstOrDefault(i => !i.IsDirectory && i.DisplayName == initialSelectionName); |
|||
if (sel != null) |
|||
SelectedItems.Add(sel); |
|||
} |
|||
|
|||
RaisePropertyChanged(nameof(QuickLinksSelectedIndex)); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
public void GoUp() |
|||
{ |
|||
var parent = Path.GetDirectoryName(Location); |
|||
if (string.IsNullOrWhiteSpace(parent)) |
|||
return; |
|||
Navigate(parent); |
|||
} |
|||
|
|||
public void Cancel() |
|||
{ |
|||
CancelRequested?.Invoke(); |
|||
} |
|||
|
|||
public void Ok() |
|||
{ |
|||
if (_selectingDirectory) |
|||
CompleteRequested?.Invoke(new[] {Location}); |
|||
else |
|||
CompleteRequested?.Invoke(SelectedItems.Select(i => i.Path).ToArray()); |
|||
} |
|||
|
|||
public void SelectSingleFile(ManagedFileChooserItemViewModel item) |
|||
{ |
|||
CompleteRequested?.Invoke(new[] {item.Path}); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
<Window xmlns="https://github.com/avaloniaui" |
|||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
|||
xmlns:dialogs="clr-namespace:Avalonia.Dialogs.Internal" |
|||
xmlns:internal="clr-namespace:Avalonia.Dialogs.Internal" |
|||
Title="{Binding Title}" MinWidth="700" MinHeight="500"> |
|||
<internal:ManagedFileChooser Margin="4"/> |
|||
</Window> |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Markup.Xaml; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
class ManagedFileDialog : Window |
|||
{ |
|||
private ManagedFileChooserViewModel _model; |
|||
public ManagedFileDialog() |
|||
{ |
|||
AvaloniaXamlLoader.Load(this); |
|||
#if DEBUG
|
|||
this.AttachDevTools(); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.Globalization; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Data.Converters; |
|||
|
|||
namespace Avalonia.Dialogs.Internal |
|||
{ |
|||
public class ResourceSelectorConverter : ResourceDictionary, IValueConverter |
|||
{ |
|||
public object Convert(object key, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
TryGetResource((string)key, out var value); |
|||
return value; |
|||
} |
|||
|
|||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Avalonia.Controls; |
|||
using Avalonia.Controls.Platform; |
|||
using Avalonia.Dialogs.Internal; |
|||
using Avalonia.Platform; |
|||
|
|||
namespace Avalonia.Dialogs |
|||
{ |
|||
public static class ManagedFileDialogExtensions |
|||
{ |
|||
class ManagedSystemDialogImpl : ISystemDialogImpl |
|||
{ |
|||
async Task<string[]> Show(SystemDialog d, IWindowImpl parent) |
|||
{ |
|||
var model = new ManagedFileChooserViewModel((FileSystemDialog)d); |
|||
|
|||
var dialog = new ManagedFileDialog |
|||
{ |
|||
DataContext = model |
|||
}; |
|||
|
|||
string[] result = null; |
|||
model.CompleteRequested += items => |
|||
{ |
|||
result = items; |
|||
dialog.Close(); |
|||
}; |
|||
model.CancelRequested += dialog.Close; |
|||
|
|||
await dialog.ShowDialog<object>(parent); |
|||
return result; |
|||
} |
|||
|
|||
public async Task<string[]> ShowFileDialogAsync(FileDialog dialog, IWindowImpl parent) |
|||
{ |
|||
return await Show(dialog, parent); |
|||
} |
|||
|
|||
public async Task<string> ShowFolderDialogAsync(OpenFolderDialog dialog, IWindowImpl parent) |
|||
{ |
|||
return (await Show(dialog, parent))?.FirstOrDefault(); |
|||
} |
|||
} |
|||
|
|||
public static TAppBuilder UseManagedSystemDialogs<TAppBuilder>(this TAppBuilder builder) |
|||
where TAppBuilder : AppBuilderBase<TAppBuilder>, new() |
|||
{ |
|||
builder.AfterPlatformServicesSetup(_ => |
|||
AvaloniaLocator.CurrentMutable.Bind<ISystemDialogImpl>().ToSingleton<ManagedSystemDialogImpl>()); |
|||
return builder; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue