diff --git a/demo/Sandbox/Views/MainWindow.axaml b/demo/Sandbox/Views/MainWindow.axaml
index d1f30fd..eced496 100644
--- a/demo/Sandbox/Views/MainWindow.axaml
+++ b/demo/Sandbox/Views/MainWindow.axaml
@@ -7,27 +7,84 @@
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Sandbox.Views.MainWindow"
x:DataType="vm:MainWindowViewModel"
+ xmlns:sys="using:System"
Icon="/Assets/avalonia-logo.ico"
Title="Sandbox">
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/demo/Ursa.Demo/Pages/PathPickerDemo.axaml b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml
new file mode 100644
index 0000000..fb3cd05
--- /dev/null
+++ b/demo/Ursa.Demo/Pages/PathPickerDemo.axaml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..307556d
--- /dev/null
+++ b/demo/Ursa.Demo/ViewModels/PathPickerDemoViewModel.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Ursa.Demo.ViewModels;
+
+public partial class PathPickerDemoViewModel : ViewModelBase
+{
+ [ObservableProperty] private string? _path;
+ [ObservableProperty] private IReadOnlyList? _paths;
+}
\ 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..07a243a
--- /dev/null
+++ b/src/Ursa.Themes.Semi/Controls/PathPicker.axaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..ff62a6b
--- /dev/null
+++ b/src/Ursa/Controls/PathPicker/PathPicker.cs
@@ -0,0 +1,211 @@
+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.Platform.Storage;
+using Avalonia.Threading;
+using Irihi.Avalonia.Shared.Common;
+
+namespace Ursa.Controls;
+
+[TemplatePart(Name = "PART_Button", Type = typeof(Button))]
+public class PathPicker : TemplatedControl
+{
+ public static readonly StyledProperty SelectedPathProperty =
+ AvaloniaProperty.Register(
+ nameof(SelectedPath), defaultBindingMode: BindingMode.TwoWay, enableDataValidation: true,
+ validate: x => string.IsNullOrWhiteSpace(x) || File.Exists(x) || Directory.Exists(x));
+
+
+ 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));
+
+ private Button? _button;
+
+ private IReadOnlyList _selectedPaths = [];
+
+ public PathPicker()
+ {
+ KeyBindings.Add(new KeyBinding
+ {
+ Command = new IRIHI_CommandBase(() =>
+ {
+ if (!SelectedPathProperty.ValidateValue!.Invoke(SelectedPath)) return;
+ SelectedPaths = string.IsNullOrWhiteSpace(SelectedPath) ? Array.Empty() : [SelectedPath!];
+ Command?.Execute(Task.FromResult(SelectedPaths));
+ }),
+ Gesture = new KeyGesture(Key.Enter)
+ });
+ }
+
+ 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);
+ }
+
+ public string? SelectedPath
+ {
+ get => GetValue(SelectedPathProperty);
+ set => SetValue(SelectedPathProperty, value);
+ }
+
+ protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+ {
+ base.OnPropertyChanged(change);
+ if (change.Property == SelectedPathsProperty)
+ SelectedPath = SelectedPaths.Count > 0 ? SelectedPaths[0] : string.Empty;
+ }
+
+ protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
+ {
+ base.OnApplyTemplate(e);
+ _button = e.NameScope.Find