diff --git a/demo/Ursa.Demo/Pages/PathPickerDemo.axaml b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml
new file mode 100644
index 0000000..50fa8c1
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/PathPickerDemo.axaml.cs b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml.cs
new file mode 100644
index 0000000..2651845
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml.cs
@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace Ursa.Demo.Pages;
+
+public partial class PathPickerDemo : UserControl
+{
+ public PathPickerDemo()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
index 6b8d953..66038c1 100644
--- a/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MainViewViewModel.cs
@@ -4,6 +4,7 @@ using Avalonia;
using Avalonia.Styling;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
+using Ursa.Controls;
using Ursa.Themes.Semi;
namespace Ursa.Demo.ViewModels;
@@ -79,6 +80,7 @@ public partial class MainViewViewModel : ViewModelBase
MenuKeys.MenuKeyTreeComboBox => new TreeComboBoxDemoViewModel(),
MenuKeys.MenuKeyTwoTonePathIcon => new TwoTonePathIconDemoViewModel(),
MenuKeys.AspectRatioLayout => new AspectRatioLayoutDemoViewModel(),
+ MenuKeys.PathPicker => new PathPickerDemoViewModel(),
_ => throw new ArgumentOutOfRangeException(nameof(s), s, null)
};
}
diff --git a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
index 9b90094..b0afd2a 100644
--- a/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
+++ b/demo/Ursa.Demo/ViewModels/MenuViewModel.cs
@@ -59,7 +59,8 @@ public class MenuViewModel : ViewModelBase
new() { MenuHeader = "ToolBar", Key = MenuKeys.MenuKeyToolBar },
new() { MenuHeader = "TreeComboBox", Key = MenuKeys.MenuKeyTreeComboBox },
new() { MenuHeader = "TwoTonePathIcon", Key = MenuKeys.MenuKeyTwoTonePathIcon },
- new() { MenuHeader = "AspectRatioLayout", Key = MenuKeys.AspectRatioLayout ,Status = "WIP"},
+ new() { MenuHeader = "AspectRatioLayout", Key = MenuKeys.AspectRatioLayout, Status = "New" },
+ new() { MenuHeader = "PathPicker", Key = MenuKeys.PathPicker, Status = "WIP" },
};
}
}
@@ -115,4 +116,5 @@ public static class MenuKeys
public const string MenuKeyTreeComboBox = "TreeComboBox";
public const string MenuKeyTwoTonePathIcon = "TwoTonePathIcon";
public const string AspectRatioLayout = "AspectRatioLayout";
+ public const string PathPicker = "PathPicker";
}
\ No newline at end of file
diff --git a/demo/Ursa.Demo/ViewModels/PathPickerDemoViewModel.cs b/demo/Ursa.Demo/ViewModels/PathPickerDemoViewModel.cs
new file mode 100644
index 0000000..6bb9d2a
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/PathPickerDemoViewModel.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class PathPickerDemoViewModel : ViewModelBase
+{
+ [ObservableProperty] private string? _path;
+ [ObservableProperty] private IReadOnlyList? _paths;
+ [ObservableProperty] private int _commandTriggerCount = 0;
+
+ [RelayCommand]
+ private void Selected(IReadOnlyList paths)
+ {
+ CommandTriggerCount++;
+ }
+}
\ No newline at end of file
diff --git a/src/Ursa.ReactiveUIExtension/Ursa.ReactiveUIExtension.csproj b/src/Ursa.ReactiveUIExtension/Ursa.ReactiveUIExtension.csproj
index b0d99e6..a40b3b8 100644
--- a/src/Ursa.ReactiveUIExtension/Ursa.ReactiveUIExtension.csproj
+++ b/src/Ursa.ReactiveUIExtension/Ursa.ReactiveUIExtension.csproj
@@ -14,14 +14,14 @@
这个是一个Ursa拓展包。这个包整合并互相兼容了UrsaWindow和UrsaView与Avalonia.ReactiveUI的功能。【Avalonia.ReactiveUI参见:https://docs.avaloniaui.net/docs/concepts/reactiveui/】
- 1.0.1
+ 1.0.2
https://github.com/irihitech/Ursa.Avalonia
true
snupkg
-
+
diff --git a/src/Ursa.Themes.Semi/Controls/PathPicker.axaml b/src/Ursa.Themes.Semi/Controls/PathPicker.axaml
new file mode 100644
index 0000000..cc0ec95
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/PathPicker.axaml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Ursa.Themes.Semi/Controls/_index.axaml b/src/Ursa.Themes.Semi/Controls/_index.axaml
index 0c8421c..541005b 100644
--- a/src/Ursa.Themes.Semi/Controls/_index.axaml
+++ b/src/Ursa.Themes.Semi/Controls/_index.axaml
@@ -55,5 +55,6 @@
+
diff --git a/src/Ursa/Controls/PathPicker/PathPicker.cs b/src/Ursa/Controls/PathPicker/PathPicker.cs
new file mode 100644
index 0000000..e4e69d2
--- /dev/null
+++ b/src/Ursa/Controls/PathPicker/PathPicker.cs
@@ -0,0 +1,310 @@
+using System.Text;
+using System.Windows.Input;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Metadata;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Logging;
+using Avalonia.Platform.Storage;
+using Avalonia.Threading;
+using Irihi.Avalonia.Shared.Common;
+using Irihi.Avalonia.Shared.Helpers;
+
+namespace Ursa.Controls;
+
+[TemplatePart(Name = "PART_Button", Type = typeof(Button))]
+public class PathPicker : TemplatedControl
+{
+ public static readonly StyledProperty SuggestedStartPathProperty =
+ AvaloniaProperty.Register(
+ nameof(SuggestedStartPath), string.Empty);
+
+ public static readonly StyledProperty UsePickerTypeProperty =
+ AvaloniaProperty.Register(
+ nameof(UsePickerType));
+
+ public static readonly StyledProperty SuggestedFileNameProperty =
+ AvaloniaProperty.Register(
+ nameof(SuggestedFileName), string.Empty);
+
+ public static readonly StyledProperty FileFilterProperty = AvaloniaProperty.Register(
+ nameof(FileFilter), string.Empty);
+
+ public static readonly StyledProperty TitleProperty = AvaloniaProperty.Register(
+ nameof(Title), string.Empty);
+
+ public static readonly StyledProperty DefaultFileExtensionProperty =
+ AvaloniaProperty.Register(
+ nameof(DefaultFileExtension), string.Empty);
+
+ public static readonly DirectProperty> SelectedPathsProperty =
+ AvaloniaProperty.RegisterDirect>(
+ nameof(SelectedPaths), o => o.SelectedPaths, (o, v) => o.SelectedPaths = v);
+
+ public static readonly StyledProperty CommandProperty = AvaloniaProperty.Register(
+ nameof(Command));
+
+ public static readonly StyledProperty AllowMultipleProperty = AvaloniaProperty.Register(
+ nameof(AllowMultiple));
+
+ public static readonly StyledProperty SelectedPathsTextProperty =
+ AvaloniaProperty.Register(
+ nameof(SelectedPathsText), defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly StyledProperty IsOmitCommandOnCancelProperty =
+ AvaloniaProperty.Register(
+ nameof(IsOmitCommandOnCancel));
+
+ public static readonly StyledProperty IsClearSelectionOnCancelProperty =
+ AvaloniaProperty.Register(
+ nameof(IsClearSelectionOnCancel));
+
+ public bool IsClearSelectionOnCancel
+ {
+ get => GetValue(IsClearSelectionOnCancelProperty);
+ set => SetValue(IsClearSelectionOnCancelProperty, value);
+ }
+
+ public bool IsOmitCommandOnCancel
+ {
+ get => GetValue(IsOmitCommandOnCancelProperty);
+ set => SetValue(IsOmitCommandOnCancelProperty, value);
+ }
+
+ public string? SelectedPathsText
+ {
+ get => GetValue(SelectedPathsTextProperty);
+ set => SetValue(SelectedPathsTextProperty, value);
+ }
+
+ private Button? _button;
+
+ private IReadOnlyList _selectedPaths = [];
+
+ public bool AllowMultiple
+ {
+ get => GetValue(AllowMultipleProperty);
+ set => SetValue(AllowMultipleProperty, value);
+ }
+
+ public ICommand? Command
+ {
+ get => GetValue(CommandProperty);
+ set => SetValue(CommandProperty, value);
+ }
+
+ public IReadOnlyList SelectedPaths
+ {
+ get => _selectedPaths;
+ private set => SetAndRaise(SelectedPathsProperty, ref _selectedPaths, value);
+ }
+
+ public string SuggestedFileName
+ {
+ get => GetValue(SuggestedFileNameProperty);
+ set => SetValue(SuggestedFileNameProperty, value);
+ }
+
+ public string DefaultFileExtension
+ {
+ get => GetValue(DefaultFileExtensionProperty);
+ set => SetValue(DefaultFileExtensionProperty, value);
+ }
+
+
+ public string Title
+ {
+ get => GetValue(TitleProperty);
+ set => SetValue(TitleProperty, value);
+ }
+
+ public string FileFilter
+ {
+ get => GetValue(FileFilterProperty);
+ set => SetValue(FileFilterProperty, value);
+ }
+
+ public UsePickerTypes UsePickerType
+ {
+ get => GetValue(UsePickerTypeProperty);
+ set => SetValue(UsePickerTypeProperty, value);
+ }
+
+ public string SuggestedStartPath
+ {
+ get => GetValue(SuggestedStartPathProperty);
+ set => SetValue(SuggestedStartPathProperty, value);
+ }
+
+ private bool _twoConvertLock;
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (_twoConvertLock) return;
+ if (change.Property == SelectedPathsProperty)
+ {
+ _twoConvertLock = true;
+ var stringBuilder = new StringBuilder();
+ if (SelectedPaths.Count != 0)
+ {
+ stringBuilder.Append(SelectedPaths[0]);
+ foreach (var item in SelectedPaths.Skip(1))
+ {
+ stringBuilder.AppendLine(item);
+ }
+ }
+
+ SelectedPathsText = stringBuilder.ToString();
+ _twoConvertLock = false;
+ }
+
+ if (change.Property == SelectedPathsTextProperty)
+ {
+ _twoConvertLock = true;
+ string[] separatedStrings = ["\r", "\n", "\r\n"];
+ // var list = SelectedPathsText?.Split(separatedStrings, StringSplitOptions.RemoveEmptyEntries)
+ // .Select(RemoveNewLine).ToArray()
+ // ?? [];
+ // if (list.Length == SelectedPaths.Count)
+ // {
+ // if (SelectedPaths.Select(x => list.Any(y => x == y)).All(x => x is false))
+ // }
+
+ SelectedPaths = SelectedPathsText?.Split(separatedStrings, StringSplitOptions.RemoveEmptyEntries)
+ .Select(RemoveNewLine).ToArray()
+ ?? [];
+ _twoConvertLock = false;
+ }
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ Button.ClickEvent.RemoveHandler(LaunchPicker, _button);
+ _button = e.NameScope.Find