Browse Source

Merge pull request #224

r3
Ruben 4 months ago
parent
commit
c2596610a5
100 changed files with 2439 additions and 2697 deletions
  1. 1 4
      src/PicView.Avalonia.MacOS/Program.cs
  2. 1 1
      src/PicView.Avalonia.MacOS/Views/AboutWindow.axaml
  3. 1 1
      src/PicView.Avalonia.MacOS/Views/BatchResizeWindow.axaml
  4. 16 6
      src/PicView.Avalonia.MacOS/Views/BatchResizeWindow.axaml.cs
  5. 1 1
      src/PicView.Avalonia.MacOS/Views/EffectsWindow.axaml
  6. 14 5
      src/PicView.Avalonia.MacOS/Views/EffectsWindow.axaml.cs
  7. 29 28
      src/PicView.Avalonia.MacOS/Views/ExifWindow.axaml
  8. 2 1
      src/PicView.Avalonia.MacOS/Views/KeybindingsWindow.axaml
  9. 152 142
      src/PicView.Avalonia.MacOS/Views/MacMainWindow.axaml
  10. 22 9
      src/PicView.Avalonia.MacOS/Views/MacMainWindow.axaml.cs
  11. 11 10
      src/PicView.Avalonia.MacOS/Views/MacOSTitlebar.axaml
  12. 4 4
      src/PicView.Avalonia.MacOS/Views/OpenWithView.axaml
  13. 3 3
      src/PicView.Avalonia.MacOS/Views/OpenWithView.axaml.cs
  14. 2 2
      src/PicView.Avalonia.MacOS/Views/SettingsWindow.axaml
  15. 1 1
      src/PicView.Avalonia.MacOS/Views/SettingsWindow.axaml.cs
  16. 1 1
      src/PicView.Avalonia.MacOS/Views/SingleImageResizeWindow.axaml
  17. 18 18
      src/PicView.Avalonia.MacOS/WindowImpl/MacOSWindow.cs
  18. 7 1
      src/PicView.Avalonia.MacOS/WindowImpl/WindowHelper.cs
  19. 1 2
      src/PicView.Avalonia.Win32/Program.cs
  20. 2 1
      src/PicView.Avalonia.Win32/Views/AboutWindow.axaml
  21. 1 1
      src/PicView.Avalonia.Win32/Views/BatchResizeResizeWindow.axaml
  22. 13 2
      src/PicView.Avalonia.Win32/Views/BatchResizeResizeWindow.axaml.cs
  23. 9 8
      src/PicView.Avalonia.Win32/Views/EffectsWindow.axaml
  24. 16 8
      src/PicView.Avalonia.Win32/Views/EffectsWindow.axaml.cs
  25. 27 25
      src/PicView.Avalonia.Win32/Views/ExifWindow.axaml
  26. 14 2
      src/PicView.Avalonia.Win32/Views/ExifWindow.axaml.cs
  27. 3 2
      src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml
  28. 2 2
      src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml
  29. 8 0
      src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml.cs
  30. 1 1
      src/PicView.Avalonia.Win32/Views/SingleImageResizeWindow.axaml
  31. 5 5
      src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml
  32. 17 7
      src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs
  33. 24 24
      src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml
  34. 48 37
      src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs
  35. 42 43
      src/PicView.Avalonia.Win32/WindowImpl/Win32Window.cs
  36. 7 1
      src/PicView.Avalonia.Win32/WindowImpl/WindowHelper.cs
  37. 5 5
      src/PicView.Avalonia/Clipboard/ClipboardFileOperations.cs
  38. 3 3
      src/PicView.Avalonia/Clipboard/ClipboardImageOperations.cs
  39. 5 10
      src/PicView.Avalonia/ColorManagement/BackgroundManager.cs
  40. 18 85
      src/PicView.Avalonia/Converters/ConversionHelper.cs
  41. 5 5
      src/PicView.Avalonia/Crop/CropDragHandler.cs
  42. 28 25
      src/PicView.Avalonia/Crop/CropFunctions.cs
  43. 4 5
      src/PicView.Avalonia/Crop/CropKeyboardManager.cs
  44. 23 23
      src/PicView.Avalonia/Crop/CropLayoutManager.cs
  45. 2 2
      src/PicView.Avalonia/Crop/CropResizeHandler.cs
  46. 5 5
      src/PicView.Avalonia/Crop/CropResizer.cs
  47. 4 4
      src/PicView.Avalonia/CustomControls/AnimatedMenu.cs
  48. 8 9
      src/PicView.Avalonia/CustomControls/AutoScrollViewer.cs
  49. 46 47
      src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs
  50. 2 1
      src/PicView.Avalonia/CustomControls/KeybindTextBox.cs
  51. 8 10
      src/PicView.Avalonia/CustomControls/PicBox.cs
  52. 3 3
      src/PicView.Avalonia/DragAndDrop/DragAndDropHelper.cs
  53. 2 2
      src/PicView.Avalonia/FileSystem/FileManager.cs
  54. 1 1
      src/PicView.Avalonia/FileSystem/FileRenamer.cs
  55. 6 7
      src/PicView.Avalonia/FileSystem/FileSaverHelper.cs
  56. 0 99
      src/PicView.Avalonia/Functions/FunctionsHelper.cs
  57. 53 39
      src/PicView.Avalonia/Functions/FunctionsMapper.cs
  58. 59 33
      src/PicView.Avalonia/Gallery/GalleryFunctions.cs
  59. 2 0
      src/PicView.Avalonia/Gallery/GalleryHelper.cs
  60. 5 6
      src/PicView.Avalonia/Gallery/GalleryLoad.cs
  61. 7 7
      src/PicView.Avalonia/Gallery/GalleryNavigation.cs
  62. 56 56
      src/PicView.Avalonia/Gallery/GalleryStretch.cs
  63. 5 5
      src/PicView.Avalonia/ImageHandling/ImageFormatConverter.cs
  64. 1 1
      src/PicView.Avalonia/ImageHandling/ImageOptimizer.cs
  65. 8 8
      src/PicView.Avalonia/ImageTransformations/Rotation/RotationNavigation.cs
  66. 9 9
      src/PicView.Avalonia/ImageTransformations/Rotation/RotationTransformer.cs
  67. 3 3
      src/PicView.Avalonia/ImageTransformations/Zoom.cs
  68. 2 2
      src/PicView.Avalonia/Input/MainKeyboardShortcuts.cs
  69. 2 2
      src/PicView.Avalonia/LockScreen/LockScreenHelper.cs
  70. 16 14
      src/PicView.Avalonia/Navigation/ErrorHandling.cs
  71. 75 66
      src/PicView.Avalonia/Navigation/ExifHandling.cs
  72. 4 3
      src/PicView.Avalonia/Navigation/FileListManager.cs
  73. 21 21
      src/PicView.Avalonia/Navigation/ImageIterator.cs
  74. 26 26
      src/PicView.Avalonia/Navigation/ImageLoader.cs
  75. 27 14
      src/PicView.Avalonia/Navigation/NavigationManager.cs
  76. 2 2
      src/PicView.Avalonia/Navigation/Slideshow.cs
  77. 41 44
      src/PicView.Avalonia/Navigation/UpdateImage.cs
  78. 1 1
      src/PicView.Avalonia/PicView.Avalonia.csproj
  79. 2 2
      src/PicView.Avalonia/Resizing/AspectRatioHelper.cs
  80. 9 9
      src/PicView.Avalonia/SettingsManagement/LanguageUpdater.cs
  81. 97 81
      src/PicView.Avalonia/SettingsManagement/SettingsUpdater.cs
  82. 18 18
      src/PicView.Avalonia/StartUp/QuickLoad.cs
  83. 28 23
      src/PicView.Avalonia/StartUp/StartUpHelper.cs
  84. 28 28
      src/PicView.Avalonia/UI/HideInterfaceLogic.cs
  85. 16 16
      src/PicView.Avalonia/UI/MenuManager.cs
  86. 45 42
      src/PicView.Avalonia/UI/TitleManager.cs
  87. 16 15
      src/PicView.Avalonia/UI/UIHelper.cs
  88. 57 0
      src/PicView.Avalonia/ViewModels/FileSortingViewModel.cs
  89. 36 0
      src/PicView.Avalonia/ViewModels/GalleryItemViewModel.cs
  90. 117 0
      src/PicView.Avalonia/ViewModels/GalleryViewModel.cs
  91. 76 101
      src/PicView.Avalonia/ViewModels/ImageCropperViewModel.cs
  92. 10 1142
      src/PicView.Avalonia/ViewModels/MainViewModel.cs
  93. 151 0
      src/PicView.Avalonia/ViewModels/MainWindowViewModel.cs
  94. 101 0
      src/PicView.Avalonia/ViewModels/NavigationViewModel.cs
  95. 326 0
      src/PicView.Avalonia/ViewModels/ToolsViewModel.cs
  96. 65 0
      src/PicView.Avalonia/ViewModels/WindowViewModel.cs
  97. 8 5
      src/PicView.Avalonia/Views/AboutView.axaml
  98. 20 20
      src/PicView.Avalonia/Views/AppearanceView.axaml
  99. 4 6
      src/PicView.Avalonia/Views/AppearanceView.axaml.cs
  100. 80 67
      src/PicView.Avalonia/Views/BatchResizeView.axaml

+ 1 - 4
src/PicView.Avalonia.MacOS/Program.cs

@@ -1,8 +1,5 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
-using Avalonia.Controls.ApplicationLifetimes;
-using Avalonia.Platform;
-using Avalonia.ReactiveUI;
 
 
 namespace PicView.Avalonia.MacOS;
 namespace PicView.Avalonia.MacOS;
 
 
@@ -22,7 +19,7 @@ internal class Program
 #if DEBUG
 #if DEBUG
             .LogToTrace()
             .LogToTrace()
 #endif
 #endif
-            .UseReactiveUI()
+            .UseR3()
             .UseAvaloniaNative()
             .UseAvaloniaNative()
             .UseSkia()
             .UseSkia()
             .With(new SkiaOptions
             .With(new SkiaOptions

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/AboutWindow.axaml

@@ -32,7 +32,7 @@
 
 
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
-                    Text="{CompiledBinding Translation.About,
+                    Text="{CompiledBinding Translation.About.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </DockPanel>
             </DockPanel>

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/BatchResizeWindow.axaml

@@ -31,7 +31,7 @@
                 VerticalAlignment="Top">
                 VerticalAlignment="Top">
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
-                    Text="{CompiledBinding Translation.ResizeImage,
+                    Text="{CompiledBinding Translation.ResizeImage.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </DockPanel>
             </DockPanel>

+ 16 - 6
src/PicView.Avalonia.MacOS/Views/BatchResizeWindow.axaml.cs

@@ -3,22 +3,27 @@ using Avalonia.Input;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.MacOS.Views;
 namespace PicView.Avalonia.MacOS.Views;
 
 
-public partial class BatchResizeWindow : Window
+public partial class BatchResizeWindow : Window, IDisposable
 {
 {
+    private readonly CompositeDisposable _disposables = new();
     public BatchResizeWindow()
     public BatchResizeWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.BatchResize + " - PicView");
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.BatchResize + " - PicView");
         Loaded += delegate
         Loaded += delegate
         {
         {
-            ClientSizeProperty.Changed.Subscribe(size =>
-            {
-                Height = 500;
-                WindowResizing.HandleWindowResize(this, size);
-            });
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(UIHelper.GetFrameProvider)
+                .Subscribe(size =>
+                {
+                    Height = 500;
+                    WindowResizing.HandleWindowResize(this, size);
+                })
+                .AddTo(_disposables);
         };
         };
     }
     }
 
 
@@ -29,4 +34,9 @@ public partial class BatchResizeWindow : Window
         var hostWindow = (Window)VisualRoot;
         var hostWindow = (Window)VisualRoot;
         hostWindow?.BeginMoveDrag(e);
         hostWindow?.BeginMoveDrag(e);
     }
     }
+    public void Dispose()
+    {
+        Disposable.Dispose(_disposables);
+        GC.SuppressFinalize(this);
+    } 
 }
 }

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/EffectsWindow.axaml

@@ -32,7 +32,7 @@
 
 
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
-                    Text="{CompiledBinding Translation.Effects,
+                    Text="{CompiledBinding Translation.Effects.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </DockPanel>
             </DockPanel>

+ 14 - 5
src/PicView.Avalonia.MacOS/Views/EffectsWindow.axaml.cs

@@ -2,13 +2,16 @@ using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Media;
 using Avalonia.Media;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.Input;
+using PicView.Avalonia.UI;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.MacOS.Views;
 namespace PicView.Avalonia.MacOS.Views;
 
 
-public partial class EffectsWindow : Window
+public partial class EffectsWindow : Window, IDisposable
 {
 {
+    private readonly CompositeDisposable _disposables = new();
     public EffectsWindow()
     public EffectsWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
@@ -21,10 +24,10 @@ public partial class EffectsWindow : Window
             MinWidth = MaxWidth = Bounds.Width;
             MinWidth = MaxWidth = Bounds.Width;
             Title = $"{TranslationManager.Translation.Effects} - PicView";
             Title = $"{TranslationManager.Translation.Effects} - PicView";
             
             
-            ClientSizeProperty.Changed.Subscribe(size =>
-            {
-                WindowResizing.HandleWindowResize(this, size);
-            });
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(UIHelper.GetFrameProvider)
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); })
+                .AddTo(_disposables);
         };
         };
         KeyDown += (_, e) =>
         KeyDown += (_, e) =>
         {
         {
@@ -44,4 +47,10 @@ public partial class EffectsWindow : Window
         var hostWindow = (Window)VisualRoot;
         var hostWindow = (Window)VisualRoot;
         hostWindow?.BeginMoveDrag(e);
         hostWindow?.BeginMoveDrag(e);
     }
     }
+    
+    public void Dispose()
+    {
+        Disposable.Dispose(_disposables);
+        GC.SuppressFinalize(this);
+    }
 }
 }

+ 29 - 28
src/PicView.Avalonia.MacOS/Views/ExifWindow.axaml

@@ -34,8 +34,8 @@
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="errorHover"
                 Classes="errorHover"
                 ClickMode="Release"
                 ClickMode="Release"
-                Command="{CompiledBinding RecycleFileCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.RecycleFileCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource RecycleGeometry}"
                 Data="{StaticResource RecycleGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -43,7 +43,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.DeleteFile,
+                ToolTip.Tip="{CompiledBinding Translation.DeleteFile.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="RecycleButton" />
                 x:Name="RecycleButton" />
@@ -52,8 +52,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{Binding OptimizeImageCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{Binding Tools.OptimizeImageCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
@@ -61,7 +61,7 @@
                 Icon="{StaticResource PortalImage}"
                 Icon="{StaticResource PortalImage}"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.OptimizeImage,
+                ToolTip.Tip="{CompiledBinding Translation.OptimizeImage.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="OptimizeButton" />
                 x:Name="OptimizeButton" />
@@ -70,8 +70,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding OpenWithCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.OpenWithCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource OpenWithGeometry}"
                 Data="{StaticResource OpenWithGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -79,7 +79,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.OpenWith,
+                ToolTip.Tip="{CompiledBinding Translation.OpenWith.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="OpenWithButton" />
                 x:Name="OpenWithButton" />
@@ -88,8 +88,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding PrintCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.PrintCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource PrintGeometry}"
                 Data="{StaticResource PrintGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -97,7 +97,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.Print,
+                ToolTip.Tip="{CompiledBinding Translation.Print.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="PrintButton" />
                 x:Name="PrintButton" />
@@ -106,15 +106,15 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding CopyImageCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.CopyImageCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Icon="{StaticResource CopyImages}"
                 Icon="{StaticResource CopyImages}"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.CopyImage,
+                ToolTip.Tip="{CompiledBinding Translation.CopyImage.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="CopyButton" />
                 x:Name="CopyButton" />
@@ -123,8 +123,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding CopyFileCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.CopyFileCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource CopyGeometry}"
                 Data="{StaticResource CopyGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -132,7 +132,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.CopyFile,
+                ToolTip.Tip="{CompiledBinding Translation.CopyFile.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="CopyFileButton" />
                 x:Name="CopyFileButton" />
@@ -141,8 +141,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding DuplicateFileCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.DuplicateFileCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource DuplicateGeometry}"
                 Data="{StaticResource DuplicateGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -150,7 +150,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.DuplicateFile,
+                ToolTip.Tip="{CompiledBinding Translation.DuplicateFile.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="DuplicateButton" />
                 x:Name="DuplicateButton" />
@@ -159,8 +159,8 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="1,0,1,0"
                 BorderThickness="1,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding LocateOnDiskCommand}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Tools.LocateOnDiskCommand}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 Data="{StaticResource ShowInFolderGeometry}"
                 Data="{StaticResource ShowInFolderGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
@@ -168,7 +168,7 @@
                 Height="28"
                 Height="28"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.ShowInFolder,
+                ToolTip.Tip="{CompiledBinding Translation.ShowInFolder.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="45"
                 Width="45"
                 x:Name="LocateOnDiskButton" />
                 x:Name="LocateOnDiskButton" />
@@ -179,15 +179,15 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="noBorderHover"
                 Classes="noBorderHover"
-                Command="{CompiledBinding SetExifRating0Command}"
-                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                Command="{CompiledBinding Exif.SetExifRating0Command}"
+                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                    FallbackValue=''}"
                                                    FallbackValue=''}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Icon="{StaticResource StarOffImage}"
                 Icon="{StaticResource StarOffImage}"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                ToolTip.Tip="{CompiledBinding Translation.RemoveStarRating,
+                ToolTip.Tip="{CompiledBinding Translation.RemoveStarRating.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="35"
                 Width="35"
                 x:Name="RemoveRatingButton" />
                 x:Name="RemoveRatingButton" />
@@ -196,7 +196,8 @@
                 Classes="txt"
                 Classes="txt"
                 DockPanel.Dock="Left"
                 DockPanel.Dock="Left"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
-                Text="{CompiledBinding Translation.ImageInfo}"
+                Text="{CompiledBinding Translation.ImageInfo.Value,
+                                       Mode=OneWay}"
                 TextAlignment="Center" />
                 TextAlignment="Center" />
 
 
         </DockPanel>
         </DockPanel>

+ 2 - 1
src/PicView.Avalonia.MacOS/Views/KeybindingsWindow.axaml

@@ -34,7 +34,8 @@
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Text="{CompiledBinding Translation.ApplicationShortcuts}"
+                    Text="{CompiledBinding Translation.ApplicationShortcuts.Value,
+                                           Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </Panel>
             </Panel>
 
 

+ 152 - 142
src/PicView.Avalonia.MacOS/Views/MacMainWindow.axaml

@@ -1,14 +1,14 @@
 <Window
 <Window
     BorderBrush="{DynamicResource SecondaryBackgroundColor}"
     BorderBrush="{DynamicResource SecondaryBackgroundColor}"
     BorderThickness="1"
     BorderThickness="1"
-    CanResize="{Binding CanResize}"
+    CanResize="{Binding MainWindow.CanResize.Value}"
     ExtendClientAreaChromeHints="SystemChrome"
     ExtendClientAreaChromeHints="SystemChrome"
     ExtendClientAreaTitleBarHeightHint="-1"
     ExtendClientAreaTitleBarHeightHint="-1"
-    MinHeight="{CompiledBinding WindowMinSize}"
-    MinWidth="{CompiledBinding WindowMinSize}"
+    MinHeight="{CompiledBinding MainWindow.WindowMinSize.Value}"
+    MinWidth="{CompiledBinding MainWindow.WindowMinSize.Value}"
     SizeChanged="Control_OnSizeChanged"
     SizeChanged="Control_OnSizeChanged"
-    SizeToContent="{CompiledBinding SizeToContent}"
-    Title="{CompiledBinding PicViewer.WindowTitle}"
+    SizeToContent="{CompiledBinding MainWindow.SizeToContent.Value}"
+    Title="{CompiledBinding PicViewer.WindowTitle.Value}"
     x:Class="PicView.Avalonia.MacOS.Views.MacMainWindow"
     x:Class="PicView.Avalonia.MacOS.Views.MacMainWindow"
     x:DataType="viewModels:MainViewModel"
     x:DataType="viewModels:MainViewModel"
     xmlns="https://github.com/avaloniaui"
     xmlns="https://github.com/avaloniaui"
@@ -22,246 +22,251 @@
     </Window.Resources>
     </Window.Resources>
     <NativeMenu.Menu>
     <NativeMenu.Menu>
         <NativeMenu>
         <NativeMenu>
-            <NativeMenuItem Header="{CompiledBinding Translation.File}">
+            <NativeMenuItem Header="{CompiledBinding Translation.File.Value}">
                 <NativeMenu>
                 <NativeMenu>
-                    <NativeMenuItem Command="{CompiledBinding OpenFileCommand}" Header="{CompiledBinding Translation.Open, Mode=OneWay}" />
-                    <!--  <NativeMenuItem  -->
-                    <!--  Command="{CompiledBinding OpenWithCommand}"  -->
-                    <!--  CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,  -->
-                    <!--  FallbackValue=''}"  -->
-                    <!--  Header="{CompiledBinding Translation.OpenWith,  -->
-                    <!--  Mode=OneWay}" />  -->
-                    <NativeMenuItem Command="{CompiledBinding SaveFileCommand}" Header="{CompiledBinding Translation.Save, Mode=OneWay}" />
-                    <NativeMenuItem Command="{CompiledBinding SaveFileAsCommand}" Header="{CompiledBinding Translation.SaveAs, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.OpenFileCommand}" Header="{CompiledBinding Translation.Open.Value, Mode=OneWay}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding PrintCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo,
+                        Command="{CompiledBinding Tools.OpenWithCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.Print,
+                        Header="{CompiledBinding Translation.OpenWith,
+                                                 Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.SaveFileCommand}" Header="{CompiledBinding Translation.Save.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.SaveFileAsCommand}" Header="{CompiledBinding Translation.SaveAs.Value, Mode=OneWay}" />
+                    <NativeMenuItem
+                        Command="{CompiledBinding Tools.PrintCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value,
+                                                           FallbackValue=''}"
+                        Header="{CompiledBinding Translation.Print.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.ImageSource,
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding LocateOnDiskCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.LocateOnDiskCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.ShowInFolder,
+                        Header="{CompiledBinding Translation.ShowInFolder.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding DeleteFileCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.DeleteFilePermanentlyCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.DeleteFile,
+                        Header="{CompiledBinding Translation.DeleteFile.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ReloadCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Navigation.ReloadCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.Reload,
+                        Header="{CompiledBinding Translation.Reload.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.ImageSource,
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
-                    <NativeMenuItem Command="{CompiledBinding NewWindowCommand}" Header="{CompiledBinding Translation.NewWindow, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.NewWindow}" Header="{CompiledBinding Translation.NewWindow.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding RenameCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.RenameCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.RenameFile,
+                        Header="{CompiledBinding Translation.RenameFile.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
                         IsEnabled="{CompiledBinding PicViewer.FileInfo,
                         IsEnabled="{CompiledBinding PicViewer.FileInfo,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding PasteCommand}" Header="{CompiledBinding Translation.Paste, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.PasteCommand}" Header="{CompiledBinding Translation.Paste.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding CopyFileCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.CopyFileCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.CopyFile,
+                        Header="{CompiledBinding Translation.CopyFile.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding CopyImageCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.CopyImageCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.CopyImage,
+                        Header="{CompiledBinding Translation.CopyImage.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.ImageSource,
+                        IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding CopyFilePathCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.CopyFilePathCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.FileCopyPath,
+                        Header="{CompiledBinding Translation.FileCopyPath.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding DuplicateFileCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.DuplicateFileCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.DuplicateFile,
+                        Header="{CompiledBinding Translation.DuplicateFile.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Image  -->
             <!--  Image  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.Image, Mode=OneWay}">
+            <NativeMenuItem Header="{CompiledBinding Translation.Image.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding RotateLeftCommand}"
-                        Header="{CompiledBinding Translation.RotateLeft,
+                        Command="{CompiledBinding Tools.RotateLeftCommand}"
+                        Header="{CompiledBinding Translation.RotateLeft.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding RotateRightCommand}"
-                        Header="{CompiledBinding Translation.RotateRight,
+                        Command="{CompiledBinding Tools.RotateRightCommand}"
+                        Header="{CompiledBinding Translation.RotateRight.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding FlipCommand}"
-                        Header="{CompiledBinding Translation.Flip,
+                        Command="{CompiledBinding Tools.FlipCommand}"
+                        Header="{CompiledBinding Translation.Flip.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding CropCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.CropCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.Crop,
+                        Header="{CompiledBinding Translation.Crop.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SetAsWallpaperCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.SetAsWallpaperCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.SetAsWallpaper,
+                        Header="{CompiledBinding Translation.SetAsWallpaper.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowExifWindowCommand}" Header="{CompiledBinding Translation.ImageInfo, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowExifWindow}" Header="{CompiledBinding Translation.ImageInfo.Value, Mode=OneWay}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding FilePropertiesCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.FilePropertiesCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.FileProperties,
+                        Header="{CompiledBinding Translation.FileProperties.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowSingleImageResizeWindowCommand}" Header="{CompiledBinding Translation.ResizeImage}" />
-                    <NativeMenuItem Command="{CompiledBinding ShowBatchResizeWindowCommand}" Header="{CompiledBinding Translation.BatchResize}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowSingleImageResizeWindow}" Header="{CompiledBinding Translation.ResizeImage.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowBatchResizeWindow}" Header="{CompiledBinding Translation.BatchResize.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding OptimizeImageCommand}"
-                        CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                        Command="{CompiledBinding Tools.OptimizeImageCommand}"
+                        CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                            FallbackValue=''}"
                                                            FallbackValue=''}"
-                        Header="{CompiledBinding Translation.OptimizeImage,
+                        Header="{CompiledBinding Translation.OptimizeImage.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                        IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                                                     Converter={x:Static ObjectConverters.IsNotNull}}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowEffectsWindowCommand}" Header="{CompiledBinding Translation.Effects, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowEffectsWindow}" Header="{CompiledBinding Translation.Effects.Value, Mode=OneWay}" />
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Navigation  -->
             <!--  Navigation  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.Navigation}">
+            <NativeMenuItem Header="{CompiledBinding Translation.Navigation.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
-                    <NativeMenuItem Command="{CompiledBinding SlideshowCommand}" Header="{CompiledBinding Translation.Slideshow}" />
+                    <NativeMenuItem
+                        Command="{CompiledBinding Tools.StartSlideShowTask}"
+                        CommandParameter="-1"
+                        Header="{CompiledBinding Translation.Slideshow.Value,
+                                                 Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding FirstCommand}" Header="{CompiledBinding Translation.FirstImage}" />
-                    <NativeMenuItem Command="{CompiledBinding LastCommand}" Header="{CompiledBinding Translation.LastImage}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.FirstCommand}" Header="{CompiledBinding Translation.FirstImage.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.LastCommand}" Header="{CompiledBinding Translation.LastImage.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding Skip10Command}" Header="{CompiledBinding Translation.AdvanceBy10Images}" />
-                    <NativeMenuItem Command="{CompiledBinding Skip100Command}" Header="{CompiledBinding Translation.AdvanceBy100Images}" />
-                    <NativeMenuItem Command="{CompiledBinding Prev10Command}" Header="{CompiledBinding Translation.GoBackBy10Images}" />
-                    <NativeMenuItem Command="{CompiledBinding Prev100Command}" Header="{CompiledBinding Translation.GoBackBy100Images}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.Skip10Command}" Header="{CompiledBinding Translation.AdvanceBy10Images.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.Skip100Command}" Header="{CompiledBinding Translation.AdvanceBy100Images.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.Prev10Command}" Header="{CompiledBinding Translation.GoBackBy10Images.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.Prev100Command}" Header="{CompiledBinding Translation.GoBackBy100Images.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding NextFolderCommand}" Header="{CompiledBinding Translation.NextFolder}" />
-                    <NativeMenuItem Command="{CompiledBinding PreviousFolderCommand}" Header="{CompiledBinding Translation.PrevFolder}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.NextFolderCommand}" Header="{CompiledBinding Translation.NextFolder.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Navigation.PreviousFolderCommand}" Header="{CompiledBinding Translation.PrevFolder.Value, Mode=OneWay}" />
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Interface configuration  -->
             <!--  Interface configuration  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.InterfaceConfiguration}">
+            <NativeMenuItem Header="{CompiledBinding Translation.InterfaceConfiguration.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
-                    <NativeMenuItem Command="{CompiledBinding ToggleFullscreenCommand}" Header="{CompiledBinding Translation.ToggleFullscreen, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding MainWindow.ToggleFullscreenCommand}" Header="{CompiledBinding Translation.ToggleFullscreen.Value, Mode=OneWay}" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ChangeAutoFitCommand}"
-                        Header="{CompiledBinding Translation.AutoFitWindow}"
-                        IsChecked="{CompiledBinding IsAutoFit}"
+                        Command="{CompiledBinding Tools.ChangeAutoFitCommand}"
+                        Header="{CompiledBinding Translation.AutoFitWindow.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsAutoFit.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
-                    <NativeMenuItem Command="{CompiledBinding ToggleUICommand}" Header="{CompiledBinding Translation.IsShowingUI, Mode=OneWay}" />
-                    <NativeMenuItem Command="{CompiledBinding ToggleGalleryCommand}" Header="{CompiledBinding Translation.ShowImageGallery, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.ToggleUICommand}" Header="{CompiledBinding Translation.IsShowingUI.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Gallery.ToggleGalleryCommand}" Header="{CompiledBinding Translation.ShowImageGallery.Value, Mode=OneWay}" />
 
 
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Sort files  -->
             <!--  Sort files  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.SortFilesBy, Mode=OneWay}">
+            <NativeMenuItem Header="{CompiledBinding Translation.SortFilesBy.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesByNameCommand}"
-                        Header="{CompiledBinding Translation.FileName,
+                        Command="{CompiledBinding Sorting.SortFilesByNameCommand}"
+                        Header="{CompiledBinding Translation.FileName.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=Name}"
                                                     ConverterParameter=Name}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesBySizeCommand}"
-                        Header="{CompiledBinding Translation.FileSize,
+                        Command="{CompiledBinding Sorting.SortFilesBySizeCommand}"
+                        Header="{CompiledBinding Translation.FileSize.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=FileSize}"
                                                     ConverterParameter=FileSize}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesByExtensionCommand}"
-                        Header="{CompiledBinding Translation.FileExtension,
+                        Command="{CompiledBinding Sorting.SortFilesByExtensionCommand}"
+                        Header="{CompiledBinding Translation.FileExtension.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=FileExtension}"
                                                     ConverterParameter=FileExtension}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesByCreationTimeCommand}"
-                        Header="{CompiledBinding Translation.Created,
+                        Command="{CompiledBinding Sorting.SortFilesByCreationTimeCommand}"
+                        Header="{CompiledBinding Translation.Created.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=Created}"
                                                     ConverterParameter=Created}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesByLastAccessTimeCommand}"
-                        Header="{CompiledBinding Translation.LastAccessTime,
+                        Command="{CompiledBinding Sorting.SortFilesByLastAccessTimeCommand}"
+                        Header="{CompiledBinding Translation.LastAccessTime.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=LastAccessTime}"
                                                     ConverterParameter=LastAccessTime}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding SortFilesRandomlyCommand}"
-                        Header="{CompiledBinding Translation.Random,
+                        Command="{CompiledBinding Sorting.SortFilesRandomlyCommand}"
+                        Header="{CompiledBinding Translation.Random.Value,
                                                  Mode=OneWay}"
                                                  Mode=OneWay}"
-                        IsChecked="{CompiledBinding SortOrder,
+                        IsChecked="{CompiledBinding Sorting.SortOrder.Value,
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     Converter={StaticResource EnumToBoolConverter},
                                                     ConverterParameter=Random}"
                                                     ConverterParameter=Random}"
                         ToggleType="Radio" />
                         ToggleType="Radio" />
@@ -269,53 +274,58 @@
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Settings  -->
             <!--  Settings  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.Settings}">
+            <NativeMenuItem Header="{CompiledBinding Translation.Settings.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
-                    <NativeMenuItem Command="{CompiledBinding ChangeBackgroundCommand}" Header="{CompiledBinding Translation.ChangeBackground, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Tools.ChangeBackgroundCommand}" Header="{CompiledBinding Translation.ChangeBackground.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ToggleLoopingCommand}"
-                        Header="{CompiledBinding Translation.IsLooping}"
-                        IsChecked="{CompiledBinding IsLooping}"
+                        Command="{CompiledBinding Tools.ToggleLoopingCommand}"
+                        Header="{CompiledBinding Translation.IsLooping.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsLooping.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding StretchCommand}"
-                        Header="{CompiledBinding Translation.Stretch}"
-                        IsChecked="{CompiledBinding IsStretched}"
+                        Command="{CompiledBinding Tools.StretchCommand}"
+                        Header="{CompiledBinding Translation.Stretch.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsStretched.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ToggleScrollCommand}"
-                        Header="{CompiledBinding Translation.ToggleScroll}"
-                        IsChecked="{CompiledBinding IsScrollingEnabled}"
+                        Command="{CompiledBinding Tools.ToggleScrollCommand}"
+                        Header="{CompiledBinding Translation.ToggleScroll.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsScrollingEnabled.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ChangeTopMostCommand}"
-                        Header="{CompiledBinding Translation.StayTopMost}"
-                        IsChecked="{CompiledBinding IsTopMost}"
+                        Command="{CompiledBinding Tools.ChangeTopMostCommand}"
+                        Header="{CompiledBinding Translation.StayTopMost.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsTopMost.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
                     <NativeMenuItem
                     <NativeMenuItem
-                        Command="{CompiledBinding ToggleSubdirectoriesCommand}"
-                        Header="{CompiledBinding Translation.SearchSubdirectory}"
-                        IsChecked="{CompiledBinding IsIncludingSubdirectories}"
+                        Command="{CompiledBinding Tools.ToggleSubdirectoriesCommand}"
+                        Header="{CompiledBinding Translation.SearchSubdirectory.Value,
+                                                 Mode=OneWay}"
+                        IsChecked="{CompiledBinding GlobalSettings.IsIncludingSubdirectories.Value}"
                         ToggleType="CheckBox" />
                         ToggleType="CheckBox" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowKeybindingsWindowCommand}" Header="{CompiledBinding Translation.ApplicationShortcuts}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowKeybindingsWindow}" Header="{CompiledBinding Translation.ApplicationShortcuts.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowAboutWindowCommand}" Header="{CompiledBinding Translation.About}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowAboutWindow}" Header="{CompiledBinding Translation.About.Value, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding ShowSettingsWindowCommand}" Header="{CompiledBinding Translation.ShowAllSettingsWindow}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.ShowSettingsWindow}" Header="{CompiledBinding Translation.ShowAllSettingsWindow.Value, Mode=OneWay}" />
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 
             <!--  Window  -->
             <!--  Window  -->
-            <NativeMenuItem Header="{CompiledBinding Translation.Window}">
+            <NativeMenuItem Header="{CompiledBinding Translation.Window.Value, Mode=OneWay}">
                 <NativeMenu>
                 <NativeMenu>
-                    <NativeMenuItem Command="{CompiledBinding NewWindowCommand}" Header="{CompiledBinding Translation.NewWindow, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding Window.NewWindow}" Header="{CompiledBinding Translation.NewWindow.Value, Mode=OneWay, Mode=OneWay}" />
                     <NativeMenuItemSeparator />
                     <NativeMenuItemSeparator />
-                    <NativeMenuItem Command="{CompiledBinding MaximizeCommand}" Header="{CompiledBinding Translation.Maximize, Mode=OneWay}" />
-                    <NativeMenuItem Command="{CompiledBinding ToggleFullscreenCommand}" Header="{CompiledBinding Translation.Fullscreen, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding MainWindow.MaximizeCommand}" Header="{CompiledBinding Translation.Maximize.Value, Mode=OneWay}" />
+                    <NativeMenuItem Command="{CompiledBinding MainWindow.ToggleFullscreenCommand}" Header="{CompiledBinding Translation.Fullscreen.Value, Mode=OneWay}" />
                 </NativeMenu>
                 </NativeMenu>
             </NativeMenuItem>
             </NativeMenuItem>
 
 

+ 22 - 9
src/PicView.Avalonia.MacOS/Views/MacMainWindow.axaml.cs

@@ -1,43 +1,50 @@
 using Avalonia.Controls;
 using Avalonia.Controls;
 using PicView.Avalonia.MacOS.WindowImpl;
 using PicView.Avalonia.MacOS.WindowImpl;
+using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
-using ReactiveUI;
+using R3;
+using R3.Avalonia;
 
 
 namespace PicView.Avalonia.MacOS.Views;
 namespace PicView.Avalonia.MacOS.Views;
 
 
 public partial class MacMainWindow : Window
 public partial class MacMainWindow : Window
 {
 {
+    private readonly AvaloniaRenderingFrameProvider _frameProvider;
+
     public MacMainWindow()
     public MacMainWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
 
 
+        _frameProvider = new AvaloniaRenderingFrameProvider(GetTopLevel(this));
+        UIHelper.SetFrameProvider(_frameProvider);
+
         Loaded += delegate
         Loaded += delegate
         {
         {
             // Keep window position when resizing
             // Keep window position when resizing
-            ClientSizeProperty.Changed.Subscribe(size =>
-            {
-                WindowResizing.HandleWindowResize(this, size);
-            });
+            ClientSizeProperty.Changed.ToObservable()
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); });
             if (DataContext is not MainViewModel vm)
             if (DataContext is not MainViewModel vm)
             {
             {
                 return;
                 return;
             }
             }
-            this.WhenAnyValue(x => x.WindowState).Subscribe(async state =>
+            Observable.EveryValueChanged(this, x => x.WindowState, _frameProvider).SubscribeAwait(async (state, _) =>
             {
             {
                 switch (state)
                 switch (state)
                 {
                 {
                     case WindowState.FullScreen:
                     case WindowState.FullScreen:
                         if (!Settings.WindowProperties.Fullscreen)
                         if (!Settings.WindowProperties.Fullscreen)
                         {
                         {
-                            await MacOSWindow.Fullscreen(this, vm); 
+                            await MacOSWindow.Fullscreen(this, vm);
                         }
                         }
+
                         break;
                         break;
                     case WindowState.Maximized:
                     case WindowState.Maximized:
                         if (!Settings.WindowProperties.Maximized)
                         if (!Settings.WindowProperties.Maximized)
                         {
                         {
-                            await MacOSWindow.Maximize(this, vm); 
+                            await MacOSWindow.Maximize(this, vm);
                         }
                         }
+
                         break;
                         break;
                     case WindowState.Normal:
                     case WindowState.Normal:
                         if (Settings.WindowProperties.Maximized || Settings.WindowProperties.Fullscreen)
                         if (Settings.WindowProperties.Maximized || Settings.WindowProperties.Fullscreen)
@@ -47,8 +54,9 @@ public partial class MacMainWindow : Window
                         break;
                         break;
                 }
                 }
             });
             });
+            
             // Hide macOS buttons when interface is hidden
             // Hide macOS buttons when interface is hidden
-            vm.WhenAnyValue(x => x.IsTopToolbarShown).Subscribe(shown =>
+            Observable.EveryValueChanged(vm, x => x.MainWindow.IsTopToolbarShown.CurrentValue, _frameProvider).Subscribe(shown =>
             {
             {
                 SystemDecorations = shown ? SystemDecorations.Full : SystemDecorations.None;
                 SystemDecorations = shown ? SystemDecorations.Full : SystemDecorations.None;
             });
             });
@@ -76,4 +84,9 @@ public partial class MacMainWindow : Window
         await WindowFunctions.WindowClosingBehavior(this);
         await WindowFunctions.WindowClosingBehavior(this);
         base.OnClosing(e);
         base.OnClosing(e);
     }
     }
+
+    protected override void OnClosed(EventArgs e)
+    {
+        _frameProvider?.Dispose();
+    }
 }
 }

+ 11 - 10
src/PicView.Avalonia.MacOS/Views/MacOSTitlebar.axaml

@@ -1,5 +1,5 @@
 <UserControl
 <UserControl
-    IsVisible="{CompiledBinding IsTopToolbarShown,
+    IsVisible="{CompiledBinding MainWindow.IsTopToolbarShown.Value,
                                 Mode=OneWay}"
                                 Mode=OneWay}"
     mc:Ignorable="d"
     mc:Ignorable="d"
     x:Class="PicView.Avalonia.MacOS.Views.MacOSTitlebar"
     x:Class="PicView.Avalonia.MacOS.Views.MacOSTitlebar"
@@ -26,23 +26,23 @@
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderThickness="0"
             BorderThickness="0"
             Classes="hover"
             Classes="hover"
-            Command="{CompiledBinding FlipCommand}"
+            Command="{CompiledBinding Tools.FlipCommand}"
             Data="{StaticResource FlipGeometry}"
             Data="{StaticResource FlipGeometry}"
             DockPanel.Dock="Right"
             DockPanel.Dock="Right"
             Foreground="{DynamicResource MainTextColor}"
             Foreground="{DynamicResource MainTextColor}"
             IconHeight="12"
             IconHeight="12"
             IconWidth="12"
             IconWidth="12"
-            IsEnabled="{CompiledBinding PicViewer.FileInfo,
+            IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
             IsRepeatEnabled="False"
             IsRepeatEnabled="False"
             IsTabStop="False"
             IsTabStop="False"
             Margin="0"
             Margin="0"
-            ToolTip.Tip="{CompiledBinding Translation.IsFlipped,
+            ToolTip.Tip="{CompiledBinding Translation.IsFlipped.Value,
                                           Mode=OneWay}"
                                           Mode=OneWay}"
             Width="30"
             Width="30"
             x:Name="FlipButton">
             x:Name="FlipButton">
             <Button.RenderTransform>
             <Button.RenderTransform>
-                <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX}" />
+                <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX.Value}" />
             </Button.RenderTransform>
             </Button.RenderTransform>
         </customControls:IconButton>
         </customControls:IconButton>
 
 
@@ -51,18 +51,19 @@
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderThickness="0"
             BorderThickness="0"
             Classes="hover"
             Classes="hover"
-            Command="{CompiledBinding RotateRightWindowBorderButtonCommand}"
+            Command="{CompiledBinding Tools.RotateRightWindowBorderButtonCommand}"
             DockPanel.Dock="Right"
             DockPanel.Dock="Right"
             Foreground="{DynamicResource MainTextColor}"
             Foreground="{DynamicResource MainTextColor}"
             Icon="{StaticResource RefreshCcwDot}"
             Icon="{StaticResource RefreshCcwDot}"
             IconHeight="13"
             IconHeight="13"
             IconWidth="13"
             IconWidth="13"
-            IsEnabled="{CompiledBinding PicViewer.FileInfo,
+            IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
             IsRepeatEnabled="False"
             IsRepeatEnabled="False"
             IsTabStop="False"
             IsTabStop="False"
             Margin="0"
             Margin="0"
-            ToolTip.Tip="{CompiledBinding Translation.RotateRight}"
+            ToolTip.Tip="{CompiledBinding Translation.RotateRight.Value,
+                                          Mode=OneWay}"
             Width="30"
             Width="30"
             x:Name="RotateRightButton" />
             x:Name="RotateRightButton" />
 
 
@@ -71,13 +72,13 @@
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderBrush="{DynamicResource MainBorderColor}"
             BorderThickness="0"
             BorderThickness="0"
             Classes="hover"
             Classes="hover"
-            Command="{CompiledBinding ToggleGalleryCommand}"
+            Command="{CompiledBinding Gallery.ToggleGalleryCommand}"
             Data="{StaticResource GalleryGeometry}"
             Data="{StaticResource GalleryGeometry}"
             DockPanel.Dock="Right"
             DockPanel.Dock="Right"
             Foreground="{DynamicResource MainTextColor}"
             Foreground="{DynamicResource MainTextColor}"
             IconHeight="17"
             IconHeight="17"
             IconWidth="17"
             IconWidth="17"
-            IsEnabled="{CompiledBinding PicViewer.FileInfo,
+            IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
                                         Converter={x:Static ObjectConverters.IsNotNull}}"
             IsRepeatEnabled="False"
             IsRepeatEnabled="False"
             IsTabStop="False"
             IsTabStop="False"

+ 4 - 4
src/PicView.Avalonia.MacOS/Views/OpenWithView.axaml

@@ -29,10 +29,10 @@
             FontFamily="avares://PicView.Avalonia/Assets/Fonts/Roboto-Medium.ttf#Roboto"
             FontFamily="avares://PicView.Avalonia/Assets/Fonts/Roboto-Medium.ttf#Roboto"
             FontSize="13"
             FontSize="13"
             FontWeight="Medium"
             FontWeight="Medium"
-            Height="{CompiledBinding TitlebarHeight,
+            Height="{CompiledBinding MainWindow.TitlebarHeight.Value,
                                      Mode=OneWay}"
                                      Mode=OneWay}"
             Padding="7"
             Padding="7"
-            Text="{CompiledBinding Translation.OpenWith,
+            Text="{CompiledBinding Translation.OpenWith.Value,
                                    Mode=OneWay}"
                                    Mode=OneWay}"
             TextAlignment="Center"
             TextAlignment="Center"
             VerticalAlignment="Top" />
             VerticalAlignment="Top" />
@@ -41,14 +41,14 @@
         </customControls:AutoScrollViewer>
         </customControls:AutoScrollViewer>
         <Button
         <Button
             Classes=" altHover"
             Classes=" altHover"
-            Height="{CompiledBinding TitlebarHeight,
+            Height="{CompiledBinding MainWindow.TitlebarHeight.Value,
                                      Mode=OneWay}"
                                      Mode=OneWay}"
             Padding="7"
             Padding="7"
             VerticalAlignment="Bottom"
             VerticalAlignment="Bottom"
             x:Name="CancelButton">
             x:Name="CancelButton">
             <TextBlock
             <TextBlock
                 Classes="txt"
                 Classes="txt"
-                Text="{CompiledBinding Translation.Cancel,
+                Text="{CompiledBinding Translation.Cancel.Value,
                                        Mode=OneWay}"
                                        Mode=OneWay}"
                 TextAlignment="Center" />
                 TextAlignment="Center" />
         </Button>
         </Button>

+ 3 - 3
src/PicView.Avalonia.MacOS/Views/OpenWithView.axaml.cs

@@ -1,12 +1,12 @@
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Layout;
 using Avalonia.Layout;
 using Avalonia.Threading;
 using Avalonia.Threading;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
-using PicView.Core.MacOS.FileAssociation;
-using Avalonia;
 using PicView.Core.MacOS.AppLauncher;
 using PicView.Core.MacOS.AppLauncher;
+using PicView.Core.MacOS.FileAssociation;
 
 
 namespace PicView.Avalonia.MacOS.Views;
 namespace PicView.Avalonia.MacOS.Views;
 
 
@@ -50,7 +50,7 @@ public partial class OpenWithView : Window
             {
             {
                 return;
                 return;
             }
             }
-            _filePath = vm.PicViewer?.FileInfo?.FullName;
+            _filePath = vm.PicViewer?.FileInfo?.CurrentValue.FullName;
         }
         }
 
 
 
 

+ 2 - 2
src/PicView.Avalonia.MacOS/Views/SettingsWindow.axaml

@@ -36,13 +36,13 @@
                     Classes="txt"
                     Classes="txt"
                     Height="28"
                     Height="28"
                     LineHeight="28"
                     LineHeight="28"
-                    Text="{CompiledBinding Translation.Settings,
+                    Text="{CompiledBinding Translation.Settings.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />
             </Panel>
             </Panel>
 
 
-            <views:SettingsView Background="{DynamicResource WindowBackgroundColor}" x:Name="XSettingsView" />
+            <views:SettingsView Background="{DynamicResource WindowBackgroundColor}" x:Name="SettingsView" />
         </StackPanel>
         </StackPanel>
     </Border>
     </Border>
 </Window>
 </Window>

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/SettingsWindow.axaml.cs

@@ -24,7 +24,7 @@ public partial class SettingsWindow : Window
         if (!Settings.Theme.Dark || Settings.Theme.GlassTheme)
         if (!Settings.Theme.Dark || Settings.Theme.GlassTheme)
         {
         {
             TitleText.Background = Brushes.Transparent;
             TitleText.Background = Brushes.Transparent;
-            XSettingsView.Background = Brushes.Transparent;
+            SettingsView.Background = Brushes.Transparent;
             SettingsButton.Background = Brushes.Transparent;
             SettingsButton.Background = Brushes.Transparent;
         }
         }
         Loaded += delegate
         Loaded += delegate

+ 1 - 1
src/PicView.Avalonia.MacOS/Views/SingleImageResizeWindow.axaml

@@ -32,7 +32,7 @@
 
 
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
-                    Text="{CompiledBinding Translation.ResizeImage,
+                    Text="{CompiledBinding Translation.ResizeImage.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </DockPanel>
             </DockPanel>

+ 18 - 18
src/PicView.Avalonia.MacOS/WindowImpl/MacOSWindow.cs

@@ -42,26 +42,26 @@ public static class MacOSWindow
 
 
         if (Settings.UIProperties.ShowInterface)
         if (Settings.UIProperties.ShowInterface)
         {
         {
-            vm.IsTopToolbarShown = true;
-            vm.IsBottomToolbarShown = Settings.UIProperties.ShowBottomNavBar;
-            vm.IsUIShown = true;
+            vm.MainWindow.IsTopToolbarShown.Value = true;
+            vm.MainWindow.IsBottomToolbarShown.Value = Settings.UIProperties.ShowBottomNavBar;
+            vm.MainWindow.IsUIShown.Value = true;
         }
         }
         else
         else
         {
         {
-            vm.IsTopToolbarShown = false;
-            vm.IsUIShown = false;
+            vm.MainWindow.IsTopToolbarShown.Value = false;
+            vm.MainWindow.IsUIShown.Value = false;
         }
         }
 
 
         if (Settings.WindowProperties.AutoFit)
         if (Settings.WindowProperties.AutoFit)
         {
         {
-            vm.SizeToContent = SizeToContent.WidthAndHeight;
-            vm.CanResize = false;
+            vm.MainWindow.SizeToContent.Value = SizeToContent.WidthAndHeight;
+            vm.MainWindow.CanResize.Value = false;
             await WindowResizing.SetSizeAsync(vm);
             await WindowResizing.SetSizeAsync(vm);
         }
         }
         else
         else
         {
         {
-            vm.SizeToContent = SizeToContent.Manual;
-            vm.CanResize = true;
+            vm.MainWindow.SizeToContent.Value = SizeToContent.Manual;
+            vm.MainWindow.CanResize.Value = true;
             WindowFunctions.InitializeWindowSizeAndPosition(window);
             WindowFunctions.InitializeWindowSizeAndPosition(window);
         }
         }
         
         
@@ -78,12 +78,12 @@ public static class MacOSWindow
         
         
         window.WindowState = WindowState.FullScreen;
         window.WindowState = WindowState.FullScreen;
         
         
-        vm.IsTopToolbarShown = false;
-        vm.IsBottomToolbarShown = false;
+        vm.MainWindow.IsTopToolbarShown.Value = false;
+        vm.MainWindow.IsBottomToolbarShown.Value = false;
         
         
-        vm.IsFullscreen = true;
-        vm.IsMaximized = false;
-        vm.CanResize = false;
+        vm.MainWindow.IsFullscreen.Value = true;
+        vm.MainWindow.IsMaximized.Value = false;
+        vm.MainWindow.CanResize.Value = false;
         await WindowResizing.SetSizeAsync(vm);
         await WindowResizing.SetSizeAsync(vm);
         
         
         if (saveSettings)
         if (saveSettings)
@@ -107,10 +107,10 @@ public static class MacOSWindow
         Settings.WindowProperties.Maximized = true;
         Settings.WindowProperties.Maximized = true;
         Settings.WindowProperties.Fullscreen = false;
         Settings.WindowProperties.Fullscreen = false;
         
         
-        vm.SizeToContent = SizeToContent.Manual;
-        vm.IsMaximized = true;
-        vm.IsFullscreen = false;
-        vm.CanResize = false;
+        vm.MainWindow.SizeToContent.Value = SizeToContent.Manual;
+        vm.MainWindow.IsMaximized.Value = true;
+        vm.MainWindow.IsFullscreen.Value = false;
+        vm.MainWindow.CanResize.Value = false;
         
         
         window.WindowState = WindowState.Maximized;
         window.WindowState = WindowState.Maximized;
         
         

+ 7 - 1
src/PicView.Avalonia.MacOS/WindowImpl/WindowHelper.cs

@@ -96,13 +96,18 @@ public class WindowManager : IPlatformSpecificUpdate
 
 
             if (_exifWindow is null)
             if (_exifWindow is null)
             {
             {
+                vm.Exif = new ExifViewModel();
                 _exifWindow = new ExifWindow
                 _exifWindow = new ExifWindow
                 {
                 {
                     DataContext = vm,
                     DataContext = vm,
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                 };
                 };
                 _exifWindow.Show(desktop.MainWindow);
                 _exifWindow.Show(desktop.MainWindow);
-                _exifWindow.Closing += (s, e) => _exifWindow = null;
+                _exifWindow.Closing += (_, _) =>
+                {
+                    _exifWindow = null;
+                    vm.Exif.Dispose();
+                };
             }
             }
             else
             else
             {
             {
@@ -195,6 +200,7 @@ public class WindowManager : IPlatformSpecificUpdate
                     DataContext = vm,
                     DataContext = vm,
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                 };
                 };
+
                 _settingsWindow.Show(desktop.MainWindow);
                 _settingsWindow.Show(desktop.MainWindow);
                 _settingsWindow.Closing += (s, e) => _settingsWindow = null;
                 _settingsWindow.Closing += (s, e) => _settingsWindow = null;
             }
             }

+ 1 - 2
src/PicView.Avalonia.Win32/Program.cs

@@ -1,6 +1,5 @@
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
-using Avalonia.ReactiveUI;
 
 
 namespace PicView.Avalonia.Win32;
 namespace PicView.Avalonia.Win32;
 
 
@@ -21,7 +20,7 @@ internal class Program
 #if DEBUG
 #if DEBUG
             .LogToTrace()
             .LogToTrace()
 #endif
 #endif
-            .UseReactiveUI()
+            .UseR3()
             .With(new SkiaOptions
             .With(new SkiaOptions
             {
             {
                 MaxGpuResourceSizeBytes = 256_000_000,
                 MaxGpuResourceSizeBytes = 256_000_000,

+ 2 - 1
src/PicView.Avalonia.Win32/Views/AboutWindow.axaml

@@ -71,7 +71,8 @@
                     Height="28"
                     Height="28"
                     LineHeight="28"
                     LineHeight="28"
                     Padding="30,0,0,0"
                     Padding="30,0,0,0"
-                    Text="{CompiledBinding Translation.About}"
+                    Text="{CompiledBinding Translation.About.Value,
+                                           Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />
             </DockPanel>
             </DockPanel>

+ 1 - 1
src/PicView.Avalonia.Win32/Views/BatchResizeResizeWindow.axaml

@@ -72,7 +72,7 @@
                     Height="{StaticResource TopBorderHeight}"
                     Height="{StaticResource TopBorderHeight}"
                     LineHeight="{StaticResource TopBorderHeight}"
                     LineHeight="{StaticResource TopBorderHeight}"
                     Padding="30,0,0,0"
                     Padding="30,0,0,0"
-                    Text="{CompiledBinding Translation.BatchResize,
+                    Text="{CompiledBinding Translation.BatchResize.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />

+ 13 - 2
src/PicView.Avalonia.Win32/Views/BatchResizeResizeWindow.axaml.cs

@@ -6,11 +6,13 @@ using Avalonia.Media;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.Win32.Views;
 namespace PicView.Avalonia.Win32.Views;
 
 
-public partial class BatchResizeWindow : Window
+public partial class BatchResizeWindow : Window, IDisposable
 {
 {
+    private readonly CompositeDisposable _disposables = new();
     public BatchResizeWindow()
     public BatchResizeWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
@@ -49,7 +51,10 @@ public partial class BatchResizeWindow : Window
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.BatchResize + " - PicView");
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.BatchResize + " - PicView");
         Loaded += delegate
         Loaded += delegate
         {
         {
-            ClientSizeProperty.Changed.Subscribe(size => { WindowResizing.HandleWindowResize(this, size); });
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(UIHelper.GetFrameProvider)
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); })
+                .AddTo(_disposables);
         };
         };
     }
     }
 
 
@@ -67,4 +72,10 @@ public partial class BatchResizeWindow : Window
     private void Close(object? sender, RoutedEventArgs e) => Close();
     private void Close(object? sender, RoutedEventArgs e) => Close();
 
 
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
+
+    public void Dispose()
+    {
+       Disposable.Dispose(_disposables);
+       GC.SuppressFinalize(this);
+    }
 }
 }

+ 9 - 8
src/PicView.Avalonia.Win32/Views/EffectsWindow.axaml

@@ -81,7 +81,7 @@
                     Width="30">
                     Width="30">
                     <customControls:IconButton.Flyout>
                     <customControls:IconButton.Flyout>
                         <MenuFlyout FlyoutPresenterClasses="noCornerRadius" Placement="Bottom">
                         <MenuFlyout FlyoutPresenterClasses="noCornerRadius" Placement="Bottom">
-                            <MenuItem Command="{CompiledBinding SaveFileCommand}" Header="{CompiledBinding Translation.Save, Mode=OneWay}">
+                            <MenuItem Command="{CompiledBinding Tools.SaveFileCommand}" Header="{CompiledBinding Translation.Save.Value, Mode=OneWay}">
                                 <MenuItem.Icon>
                                 <MenuItem.Icon>
                                     <Path
                                     <Path
                                         Data="{StaticResource SaveGeometry}"
                                         Data="{StaticResource SaveGeometry}"
@@ -91,7 +91,7 @@
                                         Width="12" />
                                         Width="12" />
                                 </MenuItem.Icon>
                                 </MenuItem.Icon>
                             </MenuItem>
                             </MenuItem>
-                            <MenuItem Command="{CompiledBinding SaveFileAsCommand}" Header="{CompiledBinding Translation.SaveAs}">
+                            <MenuItem Command="{CompiledBinding Tools.SaveFileAsCommand}" Header="{CompiledBinding Translation.SaveAs.Value, Mode=OneWay}">
                                 <MenuItem.Icon>
                                 <MenuItem.Icon>
                                     <Path
                                     <Path
                                         Data="{StaticResource SaveGeometry}"
                                         Data="{StaticResource SaveGeometry}"
@@ -101,7 +101,7 @@
                                         Width="12" />
                                         Width="12" />
                                 </MenuItem.Icon>
                                 </MenuItem.Icon>
                             </MenuItem>
                             </MenuItem>
-                            <MenuItem Command="{CompiledBinding CopyImageCommand}" Header="{CompiledBinding Translation.CopyImage, Mode=OneWay}">
+                            <MenuItem Command="{CompiledBinding Tools.CopyImageCommand}" Header="{CompiledBinding Translation.CopyImage.Value, Mode=OneWay}">
                                 <MenuItem.Icon>
                                 <MenuItem.Icon>
                                     <Path
                                     <Path
                                         Data="{StaticResource CopyGeometry}"
                                         Data="{StaticResource CopyGeometry}"
@@ -113,10 +113,10 @@
                             </MenuItem>
                             </MenuItem>
                             <Separator />
                             <Separator />
                             <MenuItem
                             <MenuItem
-                                Command="{CompiledBinding SetAsWallpaperFilledCommand}"
-                                CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                                Command="{CompiledBinding Tools.SetAsWallpaperFilledCommand}"
+                                CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                                    FallbackValue=''}"
                                                                    FallbackValue=''}"
-                                Header="{CompiledBinding Translation.SetAsWallpaper,
+                                Header="{CompiledBinding Translation.SetAsWallpaper.Value,
                                                          Mode=OneWay}">
                                                          Mode=OneWay}">
                                 <MenuItem.Icon>
                                 <MenuItem.Icon>
                                     <Path
                                     <Path
@@ -128,7 +128,7 @@
                                 </MenuItem.Icon>
                                 </MenuItem.Icon>
                             </MenuItem>
                             </MenuItem>
                             <Separator />
                             <Separator />
-                            <MenuItem Header="{CompiledBinding Translation.ClearEffects}" x:Name="ClearEffectsItem">
+                            <MenuItem Header="{CompiledBinding Translation.ClearEffects.Value, Mode=OneWay}" x:Name="ClearEffectsItem">
                                 <MenuItem.Icon>
                                 <MenuItem.Icon>
                                     <Image
                                     <Image
                                         Height="12"
                                         Height="12"
@@ -146,7 +146,8 @@
                     Height="28"
                     Height="28"
                     LineHeight="28"
                     LineHeight="28"
                     Padding="30,0,0,0"
                     Padding="30,0,0,0"
-                    Text="{CompiledBinding Translation.Effects}"
+                    Text="{CompiledBinding Translation.Effects.Value,
+                                           Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />
             </DockPanel>
             </DockPanel>

+ 16 - 8
src/PicView.Avalonia.Win32/Views/EffectsWindow.axaml.cs

@@ -6,11 +6,13 @@ using Avalonia.Media;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.Win32.Views;
 namespace PicView.Avalonia.Win32.Views;
 
 
-public partial class EffectsWindow : Window
+public partial class EffectsWindow : Window, IDisposable
 {
 {
+    private readonly CompositeDisposable _disposables = new();
     public EffectsWindow()
     public EffectsWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
@@ -46,14 +48,14 @@ public partial class EffectsWindow : Window
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.Effects + " - PicView");
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.Effects + " - PicView");
         Loaded += delegate
         Loaded += delegate
         {
         {
-            ClientSizeProperty.Changed.Subscribe(size =>
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(UIHelper.GetFrameProvider)
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); })
+                .AddTo(_disposables);
+            ClearEffectsItem.Click += delegate
             {
             {
-                WindowResizing.HandleWindowResize(this, size);
-                ClearEffectsItem.Click += delegate
-                {
-                    EffectsView?.RemoveEffects();
-                };
-            });
+                EffectsView?.RemoveEffects();
+            };
         };
         };
     }
     }
 
 
@@ -68,4 +70,10 @@ public partial class EffectsWindow : Window
     private void Close(object? sender, RoutedEventArgs e) => Close();
     private void Close(object? sender, RoutedEventArgs e) => Close();
 
 
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
+    
+    public void Dispose()
+    {
+        Disposable.Dispose(_disposables);
+        GC.SuppressFinalize(this);
+    }
 }
 }

+ 27 - 25
src/PicView.Avalonia.Win32/Views/ExifWindow.axaml

@@ -79,7 +79,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding SetExifRating0Command}"
+                    Command="{CompiledBinding Exif.SetExifRating0Command}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
                     Icon="{StaticResource StarOffImage}"
                     Icon="{StaticResource StarOffImage}"
@@ -93,8 +94,8 @@
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="errorHover"
                     Classes="errorHover"
                     ClickMode="Release"
                     ClickMode="Release"
-                    Command="{CompiledBinding RecycleFileCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.RecycleFileCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource RecycleGeometry}"
                     Data="{StaticResource RecycleGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -102,7 +103,7 @@
                     Height="28"
                     Height="28"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.DeleteFile,
+                    ToolTip.Tip="{CompiledBinding Translation.DeleteFile.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="RecycleButton" />
                     x:Name="RecycleButton" />
@@ -111,8 +112,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{Binding OptimizeImageCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{Binding Tools.OptimizeImageCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
@@ -120,7 +121,7 @@
                     Icon="{StaticResource PortalImage}"
                     Icon="{StaticResource PortalImage}"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.OptimizeImage,
+                    ToolTip.Tip="{CompiledBinding Translation.OptimizeImage.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="OptimizeButton" />
                     x:Name="OptimizeButton" />
@@ -129,8 +130,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding OpenWithCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.OpenWithCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource OpenWithGeometry}"
                     Data="{StaticResource OpenWithGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -138,7 +139,7 @@
                     Height="28"
                     Height="28"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.OpenWith,
+                    ToolTip.Tip="{CompiledBinding Translation.OpenWith.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="OpenWithButton" />
                     x:Name="OpenWithButton" />
@@ -147,8 +148,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding PrintCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.PrintCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource PrintGeometry}"
                     Data="{StaticResource PrintGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -156,7 +157,7 @@
                     Height="28"
                     Height="28"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.Print,
+                    ToolTip.Tip="{CompiledBinding Translation.Print.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="PrintButton" />
                     x:Name="PrintButton" />
@@ -165,15 +166,15 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding CopyImageCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.CopyImageCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
                     Icon="{StaticResource CopyImages}"
                     Icon="{StaticResource CopyImages}"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.CopyImage,
+                    ToolTip.Tip="{CompiledBinding Translation.CopyImage.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="CopyButton" />
                     x:Name="CopyButton" />
@@ -182,8 +183,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding CopyFileCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.CopyFileCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource CopyGeometry}"
                     Data="{StaticResource CopyGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -200,8 +201,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="0,0,1,0"
                     BorderThickness="0,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding DuplicateFileCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.DuplicateFileCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource DuplicateGeometry}"
                     Data="{StaticResource DuplicateGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -209,7 +210,7 @@
                     Height="28"
                     Height="28"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.DuplicateFile,
+                    ToolTip.Tip="{CompiledBinding Translation.DuplicateFile.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="DuplicateButton" />
                     x:Name="DuplicateButton" />
@@ -218,8 +219,8 @@
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderBrush="{DynamicResource MainBorderColor}"
                     BorderThickness="1,0,1,0"
                     BorderThickness="1,0,1,0"
                     Classes="noBorderHover"
                     Classes="noBorderHover"
-                    Command="{CompiledBinding LocateOnDiskCommand}"
-                    CommandParameter="{CompiledBinding PicViewer.FileInfo.FullName,
+                    Command="{CompiledBinding Tools.LocateOnDiskCommand}"
+                    CommandParameter="{CompiledBinding PicViewer.FileInfo.Value.FullName,
                                                        FallbackValue=''}"
                                                        FallbackValue=''}"
                     Data="{StaticResource ShowInFolderGeometry}"
                     Data="{StaticResource ShowInFolderGeometry}"
                     DockPanel.Dock="Right"
                     DockPanel.Dock="Right"
@@ -227,7 +228,7 @@
                     Height="28"
                     Height="28"
                     IconHeight="17"
                     IconHeight="17"
                     IconWidth="17"
                     IconWidth="17"
-                    ToolTip.Tip="{CompiledBinding Translation.ShowInFolder,
+                    ToolTip.Tip="{CompiledBinding Translation.ShowInFolder.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="45"
                     Width="45"
                     x:Name="LocateOnDiskButton" />
                     x:Name="LocateOnDiskButton" />
@@ -237,7 +238,8 @@
                     Classes="txt"
                     Classes="txt"
                     DockPanel.Dock="Left"
                     DockPanel.Dock="Left"
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
-                    Text="{CompiledBinding Translation.ImageInfo}"
+                    Text="{CompiledBinding Translation.ImageInfo.Value,
+                                           Mode=OneWay}"
                     TextAlignment="Center" />
                     TextAlignment="Center" />
             </DockPanel>
             </DockPanel>
             <Rectangle
             <Rectangle

+ 14 - 2
src/PicView.Avalonia.Win32/Views/ExifWindow.axaml.cs

@@ -6,14 +6,17 @@ using Avalonia.Media;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.Win32.Views;
 namespace PicView.Avalonia.Win32.Views;
 
 
-public partial class ExifWindow : Window
+public partial class ExifWindow : Window, IDisposable
 {
 {
+    private readonly CompositeDisposable _disposables = new();
     public ExifWindow()
     public ExifWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
+        
         if (Settings.Theme.GlassTheme)
         if (Settings.Theme.GlassTheme)
         {
         {
             BorderRectangle.Height = 0;
             BorderRectangle.Height = 0;
@@ -85,7 +88,10 @@ public partial class ExifWindow : Window
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.ImageInfo + " - PicView");
         GenericWindowHelper.GenericWindowInitialize(this, TranslationManager.Translation.ImageInfo + " - PicView");
         Loaded += delegate
         Loaded += delegate
         {
         {
-            ClientSizeProperty.Changed.Subscribe(size => { WindowResizing.HandleWindowResize(this, size); });
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(UIHelper.GetFrameProvider)
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); })
+                .AddTo(_disposables);
         };
         };
     }
     }
 
 
@@ -100,4 +106,10 @@ public partial class ExifWindow : Window
     private void Close(object? sender, RoutedEventArgs e) => Close();
     private void Close(object? sender, RoutedEventArgs e) => Close();
 
 
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
     private void Minimize(object? sender, RoutedEventArgs e) => WindowState = WindowState.Minimized;
+    
+    public void Dispose()
+    {
+        Disposable.Dispose(_disposables);
+        GC.SuppressFinalize(this);
+    }
 }
 }

+ 3 - 2
src/PicView.Avalonia.Win32/Views/KeybindingsWindow.axaml

@@ -45,7 +45,7 @@
                     IconHeight="10"
                     IconHeight="10"
                     IconWidth="10"
                     IconWidth="10"
                     IsRepeatEnabled="False"
                     IsRepeatEnabled="False"
-                    ToolTip.Tip="{CompiledBinding Translation.Close,
+                    ToolTip.Tip="{CompiledBinding Translation.Close.Value,
                                                   Mode=OneWay}"
                                                   Mode=OneWay}"
                     Width="30"
                     Width="30"
                     x:Name="CloseButton" />
                     x:Name="CloseButton" />
@@ -72,7 +72,8 @@
                     Foreground="{DynamicResource MainTextColor}"
                     Foreground="{DynamicResource MainTextColor}"
                     LineHeight="28"
                     LineHeight="28"
                     Padding="30,0,0,0"
                     Padding="30,0,0,0"
-                    Text="{CompiledBinding Translation.ApplicationShortcuts}"
+                    Text="{CompiledBinding Translation.ApplicationShortcuts.Value,
+                                           Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />
             </DockPanel>
             </DockPanel>

+ 2 - 2
src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml

@@ -81,7 +81,7 @@
                         IconHeight="10"
                         IconHeight="10"
                         IconWidth="10"
                         IconWidth="10"
                         IsRepeatEnabled="False"
                         IsRepeatEnabled="False"
-                        ToolTip.Tip="{CompiledBinding Translation.Close,
+                        ToolTip.Tip="{CompiledBinding Translation.Close.Value,
                                                       Mode=OneWay}"
                                                       Mode=OneWay}"
                         Width="30"
                         Width="30"
                         x:Name="CloseButton" />
                         x:Name="CloseButton" />
@@ -110,7 +110,7 @@
                         Foreground="{DynamicResource MainTextColor}"
                         Foreground="{DynamicResource MainTextColor}"
                         LineHeight="{StaticResource TopBorderHeight}"
                         LineHeight="{StaticResource TopBorderHeight}"
                         Padding="30,0,0,0"
                         Padding="30,0,0,0"
-                        Text="{CompiledBinding Translation.Settings,
+                        Text="{CompiledBinding Translation.Settings.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         TextAlignment="Center"
                         TextAlignment="Center"
                         x:Name="TitleText" />
                         x:Name="TitleText" />

+ 8 - 0
src/PicView.Avalonia.Win32/Views/SettingsWindow.axaml.cs

@@ -5,6 +5,7 @@ using Avalonia.Interactivity;
 using Avalonia.Media;
 using Avalonia.Media;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
+using PicView.Avalonia.ViewModels;
 using PicView.Core.FileAssociations;
 using PicView.Core.FileAssociations;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
 using PicView.Core.WindowsNT.FileAssociation;
 using PicView.Core.WindowsNT.FileAssociation;
@@ -75,6 +76,13 @@ public partial class SettingsWindow : Window
         {
         {
             MinWidth = Width;
             MinWidth = Width;
             Title = TranslationManager.GetTranslation("Settings") + " - PicView";
             Title = TranslationManager.GetTranslation("Settings") + " - PicView";
+            if (DataContext is not MainViewModel vm)
+            {
+                return;
+            }
+
+            GoForwardButton.Command = vm.SettingsViewModel.GoForwardCommand;
+            GoBackButton.Command = vm.SettingsViewModel.GoBackCommand;
         };
         };
         KeyDown += (_, e) =>
         KeyDown += (_, e) =>
         {
         {

+ 1 - 1
src/PicView.Avalonia.Win32/Views/SingleImageResizeWindow.axaml

@@ -72,7 +72,7 @@
                     Height="28"
                     Height="28"
                     LineHeight="28"
                     LineHeight="28"
                     Padding="30,0,0,0"
                     Padding="30,0,0,0"
-                    Text="{CompiledBinding Translation.ResizeImage,
+                    Text="{CompiledBinding Translation.ResizeImage.Value,
                                            Mode=OneWay}"
                                            Mode=OneWay}"
                     TextAlignment="Center"
                     TextAlignment="Center"
                     x:Name="TitleText" />
                     x:Name="TitleText" />

+ 5 - 5
src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml

@@ -1,11 +1,11 @@
 <Window
 <Window
-    CanResize="{CompiledBinding CanResize}"
+    CanResize="{CompiledBinding MainWindow.CanResize.Value}"
     Icon="/icon.ico"
     Icon="/icon.ico"
-    MinHeight="{CompiledBinding WindowMinSize}"
-    MinWidth="{CompiledBinding WindowMinSize}"
+    MinHeight="{CompiledBinding MainWindow.WindowMinSize.Value}"
+    MinWidth="{CompiledBinding MainWindow.WindowMinSize.Value}"
     SizeChanged="Control_OnSizeChanged"
     SizeChanged="Control_OnSizeChanged"
-    SizeToContent="{CompiledBinding SizeToContent}"
-    Title="{CompiledBinding PicViewer.WindowTitle,
+    SizeToContent="{CompiledBinding MainWindow.SizeToContent.Value}"
+    Title="{CompiledBinding PicViewer.WindowTitle.Value,
                             Mode=OneWay}"
                             Mode=OneWay}"
     TransparencyLevelHint="AcrylicBlur"
     TransparencyLevelHint="AcrylicBlur"
     x:Class="PicView.Avalonia.Win32.Views.WinMainWindow"
     x:Class="PicView.Avalonia.Win32.Views.WinMainWindow"

+ 17 - 7
src/PicView.Avalonia.Win32/Views/WinMainWindow.axaml.cs

@@ -5,16 +5,23 @@ using PicView.Avalonia.DragAndDrop;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
-using ReactiveUI;
+using R3;
+using R3.Avalonia;
 
 
 namespace PicView.Avalonia.Win32.Views;
 namespace PicView.Avalonia.Win32.Views;
 
 
 public partial class WinMainWindow : Window
 public partial class WinMainWindow : Window
 {
 {
+    private readonly AvaloniaRenderingFrameProvider _frameProvider;
+    
     public WinMainWindow()
     public WinMainWindow()
     {
     {
         InitializeComponent();
         InitializeComponent();
         
         
+        // initialize RenderingFrameProvider
+        _frameProvider = new AvaloniaRenderingFrameProvider(GetTopLevel(this)!);
+        UIHelper.SetFrameProvider(_frameProvider);
+        
         if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
         if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop)
         {
         {
             return;
             return;
@@ -28,10 +35,9 @@ public partial class WinMainWindow : Window
             }
             }
 
 
             // Keep window position when resizing
             // Keep window position when resizing
-            ClientSizeProperty.Changed.Subscribe(size =>
-            {
-                WindowResizing.HandleWindowResize(this, size);
-            });
+            ClientSizeProperty.Changed.ToObservable()
+                .ObserveOn(_frameProvider)
+                .Subscribe(size => { WindowResizing.HandleWindowResize(this, size); });
             ScalingChanged += (_, _) =>
             ScalingChanged += (_, _) =>
             {
             {
                 ScreenHelper.UpdateScreenSize(this);
                 ScreenHelper.UpdateScreenSize(this);
@@ -42,9 +48,8 @@ public partial class WinMainWindow : Window
                 DragAndDropHelper.RemoveDragDropView();
                 DragAndDropHelper.RemoveDragDropView();
             };
             };
             
             
-            this.WhenAnyValue(x => x.WindowState).Subscribe(state =>
+            Observable.EveryValueChanged(this, x => x.WindowState, _frameProvider).Subscribe( state =>
             {
             {
-
                 switch (state)
                 switch (state)
                 {
                 {
                     case WindowState.FullScreen:
                     case WindowState.FullScreen:
@@ -101,4 +106,9 @@ public partial class WinMainWindow : Window
         var wm = (MainViewModel)DataContext;
         var wm = (MainViewModel)DataContext;
         WindowResizing.SetSize(wm);
         WindowResizing.SetSize(wm);
     }
     }
+    
+    protected override void OnClosed(EventArgs e)
+    {
+        _frameProvider.Dispose();
+    }
 }
 }

+ 24 - 24
src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml

@@ -1,9 +1,9 @@
 <UserControl
 <UserControl
-    Height="{CompiledBinding TitlebarHeight,
+    Height="{CompiledBinding MainWindow.TitlebarHeight.Value,
                              Mode=OneWay}"
                              Mode=OneWay}"
-    IsVisible="{CompiledBinding IsTopToolbarShown,
+    IsVisible="{CompiledBinding MainWindow.IsTopToolbarShown.Value,
                                 Mode=OneWay}"
                                 Mode=OneWay}"
-    Margin="{CompiledBinding TopScreenMargin,
+    Margin="{CompiledBinding MainWindow.TopScreenMargin.Value,
                              Mode=OneWay}"
                              Mode=OneWay}"
     x:Class="PicView.Avalonia.Win32.Views.WinTitleBar"
     x:Class="PicView.Avalonia.Win32.Views.WinTitleBar"
     x:DataType="vm:MainViewModel"
     x:DataType="vm:MainViewModel"
@@ -15,35 +15,35 @@
     <UserControl.ContextMenu>
     <UserControl.ContextMenu>
         <ContextMenu x:Name="RotationContextMenu">
         <ContextMenu x:Name="RotationContextMenu">
             <MenuItem
             <MenuItem
-                Command="{CompiledBinding RotateToCommand}"
+                Command="{CompiledBinding Tools.RotateTask}"
                 CommandParameter="0"
                 CommandParameter="0"
                 GroupName="RotationGroup"
                 GroupName="RotationGroup"
                 Header="0°"
                 Header="0°"
                 ToggleType="Radio"
                 ToggleType="Radio"
                 x:Name="Rotation0Item" />
                 x:Name="Rotation0Item" />
             <MenuItem
             <MenuItem
-                Command="{CompiledBinding RotateToCommand}"
+                Command="{CompiledBinding Tools.RotateTask}"
                 CommandParameter="90"
                 CommandParameter="90"
                 GroupName="RotationGroup"
                 GroupName="RotationGroup"
                 Header="90°"
                 Header="90°"
                 ToggleType="Radio"
                 ToggleType="Radio"
                 x:Name="Rotation90Item" />
                 x:Name="Rotation90Item" />
             <MenuItem
             <MenuItem
-                Command="{CompiledBinding RotateToCommand}"
+                Command="{CompiledBinding Tools.RotateTask}"
                 CommandParameter="180"
                 CommandParameter="180"
                 GroupName="RotationGroup"
                 GroupName="RotationGroup"
                 Header="180°"
                 Header="180°"
                 ToggleType="Radio"
                 ToggleType="Radio"
                 x:Name="Rotation180Item" />
                 x:Name="Rotation180Item" />
             <MenuItem
             <MenuItem
-                Command="{CompiledBinding RotateToCommand}"
+                Command="{CompiledBinding Tools.RotateTask}"
                 CommandParameter="270"
                 CommandParameter="270"
                 GroupName="RotationGroup"
                 GroupName="RotationGroup"
                 Header="270°"
                 Header="270°"
                 ToggleType="Radio"
                 ToggleType="Radio"
                 x:Name="Rotation270Item" />
                 x:Name="Rotation270Item" />
             <Separator />
             <Separator />
-            <MenuItem Command="{CompiledBinding FlipCommand}" Header="{CompiledBinding Translation.IsFlipped}">
+            <MenuItem Command="{CompiledBinding Tools.FlipCommand}" Header="{CompiledBinding Translation.IsFlipped.Value}">
                 <MenuItem.Icon>
                 <MenuItem.Icon>
                     <Path
                     <Path
                         Data="{StaticResource FlipGeometry}"
                         Data="{StaticResource FlipGeometry}"
@@ -52,7 +52,7 @@
                         Stretch="Fill"
                         Stretch="Fill"
                         Width="12">
                         Width="12">
                         <Path.RenderTransform>
                         <Path.RenderTransform>
-                            <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX}" />
+                            <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX.Value}" />
                         </Path.RenderTransform>
                         </Path.RenderTransform>
                     </Path>
                     </Path>
                 </MenuItem.Icon>
                 </MenuItem.Icon>
@@ -86,13 +86,13 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="hover"
                 Classes="hover"
-                Command="{CompiledBinding ToggleGalleryCommand}"
+                Command="{CompiledBinding Gallery.ToggleGalleryCommand}"
                 Data="{StaticResource GalleryGeometry}"
                 Data="{StaticResource GalleryGeometry}"
                 DockPanel.Dock="Left"
                 DockPanel.Dock="Left"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
                 IconHeight="17"
                 IconHeight="17"
                 IconWidth="17"
                 IconWidth="17"
-                IsEnabled="{CompiledBinding PicViewer.FileInfo,
+                IsEnabled="{CompiledBinding PicViewer.FileInfo.Value,
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                 IsRepeatEnabled="False"
                 IsRepeatEnabled="False"
                 Width="30"
                 Width="30"
@@ -101,18 +101,18 @@
             <customControls:IconButton
             <customControls:IconButton
                 Background="{DynamicResource WindowButtonBackgroundColor}"
                 Background="{DynamicResource WindowButtonBackgroundColor}"
                 Classes="hover"
                 Classes="hover"
-                Command="{CompiledBinding RotateRightWindowBorderButtonCommand}"
+                Command="{CompiledBinding Tools.RotateRightWindowBorderButtonCommand}"
                 DockPanel.Dock="Left"
                 DockPanel.Dock="Left"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
                 Icon="{StaticResource RefreshCcwDot}"
                 Icon="{StaticResource RefreshCcwDot}"
                 IconHeight="16"
                 IconHeight="16"
                 IconWidth="16"
                 IconWidth="16"
-                IsEnabled="{CompiledBinding PicViewer.ImageSource,
+                IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                 IsRepeatEnabled="False"
                 IsRepeatEnabled="False"
                 Margin="0"
                 Margin="0"
                 Name="RotateRightButton"
                 Name="RotateRightButton"
-                ToolTip.Tip="{CompiledBinding Translation.RotateRight,
+                ToolTip.Tip="{CompiledBinding Translation.RotateRight.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="30"
                 Width="30"
                 x:Name="RotateLeftButton" />
                 x:Name="RotateLeftButton" />
@@ -122,22 +122,22 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="1,0,1,0"
                 BorderThickness="1,0,1,0"
                 Classes="hover"
                 Classes="hover"
-                Command="{CompiledBinding FlipCommand}"
+                Command="{CompiledBinding Tools.FlipCommand}"
                 Data="{StaticResource FlipGeometry}"
                 Data="{StaticResource FlipGeometry}"
                 DockPanel.Dock="Left"
                 DockPanel.Dock="Left"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
                 IconHeight="16"
                 IconHeight="16"
                 IconWidth="16"
                 IconWidth="16"
-                IsEnabled="{CompiledBinding PicViewer.ImageSource,
+                IsEnabled="{CompiledBinding PicViewer.ImageSource.Value,
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                                             Converter={x:Static ObjectConverters.IsNotNull}}"
                 IsRepeatEnabled="False"
                 IsRepeatEnabled="False"
                 Margin="0"
                 Margin="0"
-                ToolTip.Tip="{CompiledBinding Translation.IsFlipped,
+                ToolTip.Tip="{CompiledBinding Translation.IsFlipped.Value,
                                               Mode=OneWay}"
                                               Mode=OneWay}"
                 Width="30"
                 Width="30"
                 x:Name="FlipButton">
                 x:Name="FlipButton">
                 <Button.RenderTransform>
                 <Button.RenderTransform>
-                    <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX}" />
+                    <ScaleTransform ScaleX="{CompiledBinding PicViewer.ScaleX.Value}" />
                 </Button.RenderTransform>
                 </Button.RenderTransform>
             </customControls:IconButton>
             </customControls:IconButton>
 
 
@@ -146,7 +146,7 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 Classes="hover"
                 Classes="hover"
                 ClickMode="Release"
                 ClickMode="Release"
-                Command="{CompiledBinding ExitCommand}"
+                Command="{CompiledBinding MainWindow.ExitCommand}"
                 Data="{StaticResource CloseGeometry}"
                 Data="{StaticResource CloseGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
@@ -162,14 +162,14 @@
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="hover"
                 Classes="hover"
                 ClickMode="Release"
                 ClickMode="Release"
-                Command="{CompiledBinding ToggleFullscreenCommand}"
+                Command="{CompiledBinding MainWindow.ToggleFullscreenCommand}"
                 Data="{StaticResource FullscreenGeometry}"
                 Data="{StaticResource FullscreenGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
                 IconHeight="12"
                 IconHeight="12"
                 IconWidth="12"
                 IconWidth="12"
                 IsRepeatEnabled="False"
                 IsRepeatEnabled="False"
-                IsVisible="{CompiledBinding ShouldMaximizeBeShown,
+                IsVisible="{CompiledBinding MainWindow.ShouldMaximizeBeShown.Value,
                                             Mode=OneWay}"
                                             Mode=OneWay}"
                 Width="30"
                 Width="30"
                 x:Name="FullscreenButton" />
                 x:Name="FullscreenButton" />
@@ -179,14 +179,14 @@
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderBrush="{DynamicResource MainBorderColor}"
                 BorderThickness="0,0,1,0"
                 BorderThickness="0,0,1,0"
                 Classes="hover"
                 Classes="hover"
-                Command="{CompiledBinding RestoreCommand}"
+                Command="{CompiledBinding MainWindow.RestoreCommand}"
                 Data="{StaticResource RestoreGeometry}"
                 Data="{StaticResource RestoreGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"
                 IconHeight="12"
                 IconHeight="12"
                 IconWidth="12"
                 IconWidth="12"
                 IsRepeatEnabled="False"
                 IsRepeatEnabled="False"
-                IsVisible="{CompiledBinding !ShouldMaximizeBeShown,
+                IsVisible="{CompiledBinding !MainWindow.ShouldMaximizeBeShown.Value,
                                             Mode=OneWay}"
                                             Mode=OneWay}"
                 Width="30"
                 Width="30"
                 x:Name="RestoreButton" />
                 x:Name="RestoreButton" />
@@ -197,7 +197,7 @@
                 BorderThickness="1,0,1,0"
                 BorderThickness="1,0,1,0"
                 Classes="hover "
                 Classes="hover "
                 ClickMode="Release"
                 ClickMode="Release"
-                Command="{CompiledBinding MinimizeCommand}"
+                Command="{CompiledBinding MainWindow.MinimizeCommand}"
                 Data="{StaticResource MinimizeGeometry}"
                 Data="{StaticResource MinimizeGeometry}"
                 DockPanel.Dock="Right"
                 DockPanel.Dock="Right"
                 Foreground="{DynamicResource MainTextColor}"
                 Foreground="{DynamicResource MainTextColor}"

+ 48 - 37
src/PicView.Avalonia.Win32/Views/WinTitleBar.axaml.cs

@@ -1,12 +1,13 @@
-using System.Reactive.Linq;
 using Avalonia;
 using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Media;
 using Avalonia.Media;
 using PicView.Avalonia.DragAndDrop;
 using PicView.Avalonia.DragAndDrop;
+using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
-using ReactiveUI;
+using R3;
+using Observable = R3.Observable;
 
 
 namespace PicView.Avalonia.Win32.Views;
 namespace PicView.Avalonia.Win32.Views;
 
 
@@ -93,58 +94,68 @@ public partial class WinTitleBar : UserControl
                 return;
                 return;
             }
             }
 
 
-            this.WhenAnyValue(x => x.RotationContextMenu.IsOpen).Skip(1).Subscribe(_ =>
-            {
-                Rotation0Item.IsChecked = false;
-                Rotation90Item.IsChecked = false;
-                Rotation180Item.IsChecked = false;
-                Rotation270Item.IsChecked = false;
-                switch (vm.RotationAngle)
+            Observable.EveryValueChanged(this, x => x.RotationContextMenu.IsOpen, UIHelper.GetFrameProvider)
+                .Subscribe(_ =>
                 {
                 {
-                    case 0:
-                        Rotation0Item.IsChecked = true;
-                        break;
-                    case 90:
-                        Rotation90Item.IsChecked = true;
-                        break;
-                    case 180:
-                        Rotation180Item.IsChecked = true;
-                        break;
-                    case 270:
-                        Rotation270Item.IsChecked = true;
-                        break;
-                    
-                }
-            });
+                    UpdateRotation();
+                });
 
 
             RotateLeftButton.PointerPressed += (_, e) =>
             RotateLeftButton.PointerPressed += (_, e) =>
             {
             {
-                if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
-                {
-                    // Context menu doesn't want to be opened normally
-                    RotationContextMenu.Open();
-                    return;
-                }
+                OpenContextMenu(e);
             };
             };
             FlipButton.PointerPressed += (_, e) =>
             FlipButton.PointerPressed += (_, e) =>
             {
             {
-                if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
-                {
-                    // Context menu doesn't want to be opened normally
-                    RotationContextMenu.Open();
-                    return;
-                }
+                OpenContextMenu(e);
             };
             };
             
             
         };
         };
+    }
+
+    private void OpenContextMenu(PointerPressedEventArgs e)
+    {
+        if (!e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
+        {
+            return;
+        }
 
 
+        // Context menu doesn't want to be opened normally
+        RotationContextMenu.Open();
+    }
+
+    private void UpdateRotation()
+    {
+        if (DataContext is not MainViewModel vm)
+        {
+            return;
+        }
+        Rotation0Item.IsChecked = false;
+        Rotation90Item.IsChecked = false;
+        Rotation180Item.IsChecked = false;
+        Rotation270Item.IsChecked = false;
+        switch (vm.GlobalSettings.RotationAngle.CurrentValue)
+        {
+            case 0:
+                Rotation0Item.IsChecked = true;
+                break;
+            case 90:
+                Rotation90Item.IsChecked = true;
+                break;
+            case 180:
+                Rotation180Item.IsChecked = true;
+                break;
+            case 270:
+                Rotation270Item.IsChecked = true;
+                break;
+                    
+        }
     }
     }
 
 
     private void MoveWindow(PointerPressedEventArgs e)
     private void MoveWindow(PointerPressedEventArgs e)
     {
     {
         if (VisualRoot is null || DataContext is not MainViewModel vm) { return; }
         if (VisualRoot is null || DataContext is not MainViewModel vm) { return; }
         
         
-        if (vm.IsEditableTitlebarOpen)
+        if (vm.MainWindow.IsEditableTitlebarOpen.Value)
         {
         {
             return;
             return;
         }
         }

+ 42 - 43
src/PicView.Avalonia.Win32/WindowImpl/Win32Window.cs

@@ -18,10 +18,10 @@ public static class Win32Window
         MenuManager.CloseMenus(vm);
         MenuManager.CloseMenus(vm);
 
 
         // Update view model properties
         // Update view model properties
-        vm.SizeToContent = SizeToContent.Manual;
-        vm.IsFullscreen = true;
-        vm.IsMaximized = false;
-        vm.CanResize = false;
+        vm.MainWindow.SizeToContent.Value = SizeToContent.Manual;
+        vm.MainWindow.IsFullscreen.Value = true;
+        vm.MainWindow.IsMaximized.Value = false;
+        vm.MainWindow.CanResize.Value = false;
 
 
         // Update settings
         // Update settings
         Settings.WindowProperties.Fullscreen = true;
         Settings.WindowProperties.Fullscreen = true;
@@ -31,14 +31,14 @@ public static class Win32Window
 
 
         // Hide interface in fullscreen
         // Hide interface in fullscreen
         HideInterface(vm);
         HideInterface(vm);
+        
+        vm.PicViewer.GalleryWidth.Value = double.NaN;
+        
+        await WindowResizing.SetSizeAsync(vm);
 
 
         // Center it, to make sure it is positioned correctly
         // Center it, to make sure it is positioned correctly
         CenterWindowOnScreen(window);
         CenterWindowOnScreen(window);
 
 
-        await WindowResizing.SetSizeAsync(vm);
-
-        vm.GalleryWidth = double.NaN;
-
         if (saveSettings)
         if (saveSettings)
         {
         {
             await SaveSettingsAsync().ConfigureAwait(false);
             await SaveSettingsAsync().ConfigureAwait(false);
@@ -54,7 +54,7 @@ public static class Win32Window
         {
         {
             if (Settings.WindowProperties.AutoFit)
             if (Settings.WindowProperties.AutoFit)
             {
             {
-                vm.SizeToContent = SizeToContent.Manual;
+                vm.MainWindow.SizeToContent.Value = SizeToContent.Manual;
             }
             }
             
             
             // Save window size, so that restoring it will return to the same size and position
             // Save window size, so that restoring it will return to the same size and position
@@ -66,9 +66,9 @@ public static class Win32Window
             SetMargin(vm, window);
             SetMargin(vm, window);
         });
         });
 
 
-        vm.IsMaximized = true;
-        vm.IsFullscreen = false;
-        vm.CanResize = false;
+        vm.MainWindow.IsMaximized.Value = true;
+        vm.MainWindow.IsFullscreen.Value = false;
+        vm.MainWindow.CanResize.Value = false;
 
 
         if (saveSettings)
         if (saveSettings)
         {
         {
@@ -84,8 +84,8 @@ public static class Win32Window
 
 
         // Update UI state
         // Update UI state
         SetMargin(vm, window);
         SetMargin(vm, window);
-        vm.IsMaximized = false;
-        vm.IsFullscreen = false;
+        vm.MainWindow.IsMaximized.Value = false;
+        vm.MainWindow.IsFullscreen.Value = false;
 
 
         RestoreInterface(vm);
         RestoreInterface(vm);
 
 
@@ -94,28 +94,27 @@ public static class Win32Window
 
 
         if (Settings.WindowProperties.AutoFit)
         if (Settings.WindowProperties.AutoFit)
         {
         {
-            vm.SizeToContent = SizeToContent.WidthAndHeight;
-            vm.CanResize = false;
-            vm.IsAutoFit = true;
-            if (Settings.WindowProperties.KeepCentered)
-            {
-                WindowFunctions.CenterWindowOnScreen();
-            }
-            else
-            {
-                WindowFunctions.InitializeWindowSizeAndPosition(window);
-            }
-
-            await WindowFunctions.ResizeAndFixRenderingError(vm); // Fixes incorrect render size
+            vm.MainWindow.SizeToContent.Value = SizeToContent.WidthAndHeight;
+            vm.MainWindow.CanResize.Value = false;
+            vm.GlobalSettings.IsAutoFit.Value = true;
         }
         }
         else
         else
         {
         {
-            vm.SizeToContent = SizeToContent.Manual;
-            vm.CanResize = true;
-            WindowFunctions.InitializeWindowSizeAndPosition(window);
+            vm.MainWindow.SizeToContent.Value = SizeToContent.Manual;
+            vm.MainWindow.CanResize.Value = true;
+            vm.GlobalSettings.IsAutoFit.Value = false;
         }
         }
         
         
         await WindowResizing.SetSizeAsync(vm);
         await WindowResizing.SetSizeAsync(vm);
+        
+        if (Settings.WindowProperties.KeepCentered)
+        {
+            WindowFunctions.CenterWindowOnScreen();
+        }
+        else
+        {
+            WindowFunctions.InitializeWindowSizeAndPosition(window);
+        }
 
 
         if (saveSettings)
         if (saveSettings)
         {
         {
@@ -195,7 +194,7 @@ public static class Win32Window
 
 
             // Set the window's new position
             // Set the window's new position
             window.Position = new PixelPoint((int)centeredX, (int)centeredY);
             window.Position = new PixelPoint((int)centeredX, (int)centeredY);
-        });
+        },DispatcherPriority.Background);
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -203,23 +202,23 @@ public static class Win32Window
     /// </summary>
     /// </summary>
     private static void RestoreInterface(MainViewModel vm)
     private static void RestoreInterface(MainViewModel vm)
     {
     {
-        vm.IsUIShown = Settings.UIProperties.ShowInterface;
+        vm.MainWindow.IsUIShown.Value = Settings.UIProperties.ShowInterface;
 
 
         if (!Settings.UIProperties.ShowInterface)
         if (!Settings.UIProperties.ShowInterface)
         {
         {
             return;
             return;
         }
         }
 
 
-        vm.IsTopToolbarShown = true;
-        vm.TitlebarHeight = SizeDefaults.MainTitlebarHeight;
+        vm.MainWindow.IsTopToolbarShown.Value = true;
+        vm.MainWindow.TitlebarHeight.Value = SizeDefaults.MainTitlebarHeight;
 
 
         if (!Settings.UIProperties.ShowBottomNavBar)
         if (!Settings.UIProperties.ShowBottomNavBar)
         {
         {
             return;
             return;
         }
         }
 
 
-        vm.IsBottomToolbarShown = true;
-        vm.BottombarHeight = SizeDefaults.BottombarHeight;
+        vm.MainWindow.IsBottomToolbarShown.Value = true;
+        vm.MainWindow.BottombarHeight.Value = SizeDefaults.BottombarHeight;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -227,9 +226,9 @@ public static class Win32Window
     /// </summary>
     /// </summary>
     private static void HideInterface(MainViewModel vm)
     private static void HideInterface(MainViewModel vm)
     {
     {
-        vm.IsTopToolbarShown = false;
-        vm.IsBottomToolbarShown = false;
-        vm.IsUIShown = false;
+        vm.MainWindow.IsTopToolbarShown.Value = false;
+        vm.MainWindow.IsBottomToolbarShown.Value = false;
+        vm.MainWindow.IsUIShown.Value = false;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -244,14 +243,14 @@ public static class Win32Window
             var top = window.OffScreenMargin.Top is 0 ? 7 : window.OffScreenMargin.Top;
             var top = window.OffScreenMargin.Top is 0 ? 7 : window.OffScreenMargin.Top;
             var right = window.OffScreenMargin.Right is 0 ? 7 : window.OffScreenMargin.Right;
             var right = window.OffScreenMargin.Right is 0 ? 7 : window.OffScreenMargin.Right;
             var bottom = window.OffScreenMargin.Bottom is 0 ? 7 : window.OffScreenMargin.Bottom;
             var bottom = window.OffScreenMargin.Bottom is 0 ? 7 : window.OffScreenMargin.Bottom;
-            vm.TopScreenMargin = new Thickness(left, top, right, 0);
-            vm.BottomScreenMargin = new Thickness(left, 0, right, bottom);
+            vm.MainWindow.TopScreenMargin.Value = new Thickness(left, top, right, 0);
+            vm.MainWindow.BottomScreenMargin.Value = new Thickness(left, 0, right, bottom);
         }
         }
         else
         else
         {
         {
             var noThickness = new Thickness(0);
             var noThickness = new Thickness(0);
-            vm.TopScreenMargin = noThickness;
-            vm.BottomScreenMargin = noThickness;
+            vm.MainWindow.TopScreenMargin.Value = noThickness;
+            vm.MainWindow.BottomScreenMargin.Value = noThickness;
         }
         }
     }
     }
 
 

+ 7 - 1
src/PicView.Avalonia.Win32/WindowImpl/WindowHelper.cs

@@ -96,13 +96,18 @@ public class WindowManager : IPlatformSpecificUpdate
 
 
             if (_exifWindow is null)
             if (_exifWindow is null)
             {
             {
+                vm.Exif = new ExifViewModel();
                 _exifWindow = new ExifWindow
                 _exifWindow = new ExifWindow
                 {
                 {
                     DataContext = vm,
                     DataContext = vm,
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                 };
                 };
                 _exifWindow.Show(desktop.MainWindow);
                 _exifWindow.Show(desktop.MainWindow);
-                _exifWindow.Closing += (s, e) => _exifWindow = null;
+                _exifWindow.Closing += (_, _) =>
+                {
+                    _exifWindow = null;
+                    vm.Exif.Dispose();
+                };
             }
             }
             else
             else
             {
             {
@@ -195,6 +200,7 @@ public class WindowManager : IPlatformSpecificUpdate
                     DataContext = vm,
                     DataContext = vm,
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                     WindowStartupLocation = WindowStartupLocation.CenterOwner
                 };
                 };
+
                 _settingsWindow.Show(desktop.MainWindow);
                 _settingsWindow.Show(desktop.MainWindow);
                 _settingsWindow.Closing += (s, e) => _settingsWindow = null;
                 _settingsWindow.Closing += (s, e) => _settingsWindow = null;
             }
             }

+ 5 - 5
src/PicView.Avalonia/Clipboard/ClipboardFileOperations.cs

@@ -31,9 +31,9 @@ public static class ClipboardFileOperations
         
         
         try
         try
         {
         {
-            vm.IsLoading = true;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = true;
             
             
-            if (path == vm.PicViewer.FileInfo?.FullName)
+            if (path == vm.PicViewer.FileInfo?.CurrentValue.FullName)
             {
             {
                 await DuplicateCurrentFile(vm);
                 await DuplicateCurrentFile(vm);
             }
             }
@@ -51,7 +51,7 @@ public static class ClipboardFileOperations
         }
         }
         finally
         finally
         {
         {
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         }
         }
     }
     }
 
 
@@ -66,8 +66,8 @@ public static class ClipboardFileOperations
             return;
             return;
         }
         }
 
 
-        var oldPath = vm.PicViewer.FileInfo.FullName;
-        var duplicatedPath = await FileHelper.DuplicateAndReturnFileNameAsync(oldPath, vm.PicViewer.FileInfo);
+        var oldPath = vm.PicViewer.FileInfo.CurrentValue.FullName;
+        var duplicatedPath = await FileHelper.DuplicateAndReturnFileNameAsync(oldPath, vm.PicViewer.FileInfo.CurrentValue);
 
 
         if (string.IsNullOrWhiteSpace(duplicatedPath) || !File.Exists(duplicatedPath))
         if (string.IsNullOrWhiteSpace(duplicatedPath) || !File.Exists(duplicatedPath))
         {
         {

+ 3 - 3
src/PicView.Avalonia/Clipboard/ClipboardImageOperations.cs

@@ -23,7 +23,7 @@ public static class ClipboardImageOperations
     public static async Task<bool> CopyImageToClipboard(MainViewModel vm)
     public static async Task<bool> CopyImageToClipboard(MainViewModel vm)
     {
     {
         var clipboard = ClipboardService.GetClipboard();
         var clipboard = ClipboardService.GetClipboard();
-        if (clipboard == null || vm.PicViewer.ImageSource is not Bitmap bitmap)
+        if (clipboard == null || vm.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
         {
         {
             return false;
             return false;
         }
         }
@@ -93,12 +93,12 @@ public static class ClipboardImageOperations
             return Convert.ToBase64String(await File.ReadAllBytesAsync(path));
             return Convert.ToBase64String(await File.ReadAllBytesAsync(path));
         }
         }
 
 
-        switch (vm.PicViewer.ImageType)
+        switch (vm.PicViewer.ImageType.CurrentValue)
         {
         {
             case ImageType.AnimatedGif:
             case ImageType.AnimatedGif:
             case ImageType.AnimatedWebp:
             case ImageType.AnimatedWebp:
             case ImageType.Bitmap:
             case ImageType.Bitmap:
-                if (vm.PicViewer.ImageSource is not Bitmap bitmap)
+                if (vm.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
                 {
                 {
                     return string.Empty;
                     return string.Empty;
                 }
                 }

+ 5 - 10
src/PicView.Avalonia/ColorManagement/BackgroundManager.cs

@@ -72,11 +72,6 @@ public static class BackgroundManager
     /// <param name="vm">The main view model where the background is updated.</param>
     /// <param name="vm">The main view model where the background is updated.</param>
     public static void ChangeBackground(MainViewModel vm)
     public static void ChangeBackground(MainViewModel vm)
     {
     {
-        if (vm.CurrentView != vm.ImageViewer)
-        {
-            return;
-        }
-
         // Cycle to the next background choice
         // Cycle to the next background choice
         var nextChoice = (Settings.UIProperties.BgColorChoice + 1) % ((int)BackgroundType.MaxValue + 1);
         var nextChoice = (Settings.UIProperties.BgColorChoice + 1) % ((int)BackgroundType.MaxValue + 1);
         SetBackground(vm, nextChoice);
         SetBackground(vm, nextChoice);
@@ -109,16 +104,16 @@ public static class BackgroundManager
         Settings.UIProperties.BgColorChoice = choice;
         Settings.UIProperties.BgColorChoice = choice;
         if (Settings.UIProperties.IsConstrainBackgroundColorEnabled)
         if (Settings.UIProperties.IsConstrainBackgroundColorEnabled)
         {
         {
-            vm.ImageBackground = new SolidColorBrush(Colors.Transparent);
-            vm.ConstrainedImageBackground = GetBackgroundBrush((BackgroundType)choice);
+            vm.MainWindow.ImageBackground.Value = new SolidColorBrush(Colors.Transparent);
+            vm.MainWindow.ConstrainedImageBackground.Value = GetBackgroundBrush((BackgroundType)choice);
         }
         }
         else
         else
         {
         {
-            vm.ImageBackground = GetBackgroundBrush((BackgroundType)choice);
-            vm.ConstrainedImageBackground = new SolidColorBrush(Colors.Transparent);
+            vm.MainWindow.ImageBackground.Value = GetBackgroundBrush((BackgroundType)choice);
+            vm.MainWindow.ConstrainedImageBackground.Value = new SolidColorBrush(Colors.Transparent);
         }
         }
         
         
-        vm.BackgroundChoice = choice;
+        vm.MainWindow.BackgroundChoice.Value = choice;
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 18 - 85
src/PicView.Avalonia/Converters/ConversionHelper.cs

@@ -1,41 +1,11 @@
-using ImageMagick;
-using PicView.Avalonia.Interfaces;
-using PicView.Avalonia.Navigation;
-using PicView.Avalonia.UI;
-using PicView.Avalonia.ViewModels;
+using PicView.Avalonia.ViewModels;
+using PicView.Core.DebugTools;
 using PicView.Core.ImageDecoding;
 using PicView.Core.ImageDecoding;
 
 
 namespace PicView.Avalonia.Converters;
 namespace PicView.Avalonia.Converters;
 
 
 internal static class ConversionHelper
 internal static class ConversionHelper
 {
 {
-    public static async Task<bool> ResizeImageByPercentage(FileInfo fileInfo, int selectedIndex)
-    {
-        var percentage = 100 - selectedIndex * 5;
-
-        if (percentage is < 5 or > 100)
-        {
-            return false;
-        }
-
-        var magickPercentage = new Percentage(percentage);
-        return await SaveImageFileHelper.ResizeImageAsync(fileInfo, 0, 0, 100, magickPercentage).ConfigureAwait(false);
-    }
-    
-    public static async Task ResizeImageByPercentage(int percentage, MainViewModel vm)
-    {
-        TitleManager.SetLoadingTitle(vm);
-        var success = await ResizeImageByPercentage(vm.PicViewer.FileInfo, percentage);
-        if (success)
-        {
-            await NavigationManager.QuickReload();
-        }
-        else
-        {
-            TitleManager.SetTitle(vm);
-        }
-    }
-
     public static async Task<bool> ResizeByWidth(FileInfo fileInfo, double width)
     public static async Task<bool> ResizeByWidth(FileInfo fileInfo, double width)
     {
     {
         if (width <= 0)
         if (width <= 0)
@@ -56,69 +26,32 @@ internal static class ConversionHelper
         return await SaveImageFileHelper.ResizeImageAsync(fileInfo, 0, (uint)height).ConfigureAwait(false);
         return await SaveImageFileHelper.ResizeImageAsync(fileInfo, 0, (uint)height).ConfigureAwait(false);
     }
     }
 
 
-    public static async Task<string> ConvertTask(FileInfo fileInfo, int selectedIndex, IPlatformSpecificService platform)
-    {
-        var currentExtension = fileInfo.Extension.ToLower();
-        var newExtension = selectedIndex switch
-        {
-            1 => ".png",
-            2 => ".jpg",
-            3 => ".webp",
-            4 => ".avif",
-            5 => ".heic",
-            6 => ".jxl",
-            _ => currentExtension
-        };
-        if (currentExtension == newExtension)
-        {
-            return string.Empty;
-        }
-        var oldPath = fileInfo.FullName;
-        var newPath = Path.ChangeExtension(fileInfo.FullName, newExtension);
-
-        var success = await SaveImageFileHelper.SaveImageAsync(null, oldPath, null, null, null, null,
-            newExtension);
-        if (!success)
-        {
-            return string.Empty;
-        }
-
-        await platform.DeleteFile(oldPath, true);
-        return newPath;
-    }
-    
-    public static async Task ConvertFileExtension(int index, MainViewModel vm)
-    {
-        if (vm.PicViewer.FileInfo is null)
-        {
-            return;
-        }
-
-        var newPath = await ConvertTask(vm.PicViewer.FileInfo, index, vm.PlatformService);
-        if (!string.IsNullOrWhiteSpace(newPath))
-        {
-            await NavigationManager.LoadPicFromStringAsync(newPath, vm);
-        }
-    }
-    
     public static void DetermineIfOptimizeImageShouldBeEnabled(MainViewModel vm)
     public static void DetermineIfOptimizeImageShouldBeEnabled(MainViewModel vm)
     {
     {
         if (vm.PicViewer.FileInfo is null)
         if (vm.PicViewer.FileInfo is null)
         {
         {
-            vm.ShouldOptimizeImageBeEnabled = false;
+            vm.GlobalSettings.ShouldOptimizeImageBeEnabled.Value = false;
             return;
             return;
         }
         }
 
 
-        if (vm.PicViewer.FileInfo.Extension.Equals(".jpg", StringComparison.InvariantCultureIgnoreCase)
-            || vm.PicViewer.FileInfo.Extension.Equals(".jpeg", StringComparison.InvariantCultureIgnoreCase)
-            || vm.PicViewer.FileInfo.Extension.Equals(".png", StringComparison.InvariantCultureIgnoreCase)
-            || vm.PicViewer.FileInfo.Extension.Equals(".gif", StringComparison.InvariantCultureIgnoreCase))
+        try
         {
         {
-            vm.ShouldOptimizeImageBeEnabled = true;
+            if (vm.PicViewer.FileInfo.CurrentValue.Extension.Equals(".jpg", StringComparison.InvariantCultureIgnoreCase)
+                || vm.PicViewer.FileInfo.CurrentValue.Extension.Equals(".jpeg", StringComparison.InvariantCultureIgnoreCase)
+                || vm.PicViewer.FileInfo.CurrentValue.Extension.Equals(".png", StringComparison.InvariantCultureIgnoreCase)
+                || vm.PicViewer.FileInfo.CurrentValue.Extension.Equals(".gif", StringComparison.InvariantCultureIgnoreCase))
+            {
+                vm.GlobalSettings.ShouldOptimizeImageBeEnabled.Value = true;
+            }
+            else
+            {
+                vm.GlobalSettings.ShouldOptimizeImageBeEnabled.Value = false;
+            }
         }
         }
-        else
+        catch (Exception e)
         {
         {
-            vm.ShouldOptimizeImageBeEnabled = false;
+            DebugHelper.LogDebug(nameof(ConversionHelper), nameof(DetermineIfOptimizeImageShouldBeEnabled), e);
+            vm.GlobalSettings.ShouldOptimizeImageBeEnabled.Value = false;
         }
         }
     }
     }
 }
 }

+ 5 - 5
src/PicView.Avalonia/Crop/CropDragHandler.cs

@@ -39,7 +39,7 @@ public class CropDragHandler(CropControl control)
             currentTop = 0;
             currentTop = 0;
         }
         }
 
 
-        _originalRect = new Rect(currentLeft, currentTop, vm.Crop.SelectionWidth, vm.Crop.SelectionHeight);
+        _originalRect = new Rect(currentLeft, currentTop, vm.Crop.SelectionWidth.CurrentValue, vm.Crop.SelectionHeight.CurrentValue);
         _isDragging = true;
         _isDragging = true;
     }
     }
 
 
@@ -68,8 +68,8 @@ public class CropDragHandler(CropControl control)
         var newTop = _originalRect.Y + delta.Y;
         var newTop = _originalRect.Y + delta.Y;
 
 
         // Clamp the newLeft and newTop values to keep the rectangle within bounds
         // Clamp the newLeft and newTop values to keep the rectangle within bounds
-        newLeft = Math.Max(0, Math.Min(vm.PicViewer.ImageWidth - vm.Crop.SelectionWidth, newLeft));
-        newTop = Math.Max(0, Math.Min(vm.PicViewer.ImageHeight - vm.Crop.SelectionHeight, newTop));
+        newLeft = Math.Max(0, Math.Min(vm.PicViewer.ImageWidth.CurrentValue - vm.Crop.SelectionWidth.CurrentValue, newLeft));
+        newTop = Math.Max(0, Math.Min(vm.PicViewer.ImageHeight.CurrentValue - vm.Crop.SelectionHeight.CurrentValue, newTop));
 
 
         // Only proceed if new positions are valid (i.e., not NaN)
         // Only proceed if new positions are valid (i.e., not NaN)
         if (double.IsNaN(newLeft) || double.IsNaN(newTop))
         if (double.IsNaN(newLeft) || double.IsNaN(newTop))
@@ -85,8 +85,8 @@ public class CropDragHandler(CropControl control)
         Canvas.SetTop(control.SizeBorder, newTop - control.SizeBorder.Bounds.Height - ButtonTopOffset);
         Canvas.SetTop(control.SizeBorder, newTop - control.SizeBorder.Bounds.Height - ButtonTopOffset);
 
 
         // Update view model values
         // Update view model values
-        vm.Crop.SelectionX = Convert.ToInt32(newLeft);
-        vm.Crop.SelectionY = Convert.ToInt32(newTop);
+        vm.Crop.SelectionX.Value = Convert.ToInt32(newLeft);
+        vm.Crop.SelectionY.Value = Convert.ToInt32(newTop);
     }
     }
 
 
     public void OnDragEnd(object? sender, PointerReleasedEventArgs e)
     public void OnDragEnd(object? sender, PointerReleasedEventArgs e)

+ 28 - 25
src/PicView.Avalonia/Crop/CropFunctions.cs

@@ -33,7 +33,7 @@ public static class CropFunctions
             return;
             return;
         }
         }
 
 
-        if (vm?.PicViewer.ImageSource is not Bitmap bitmap)
+        if (vm?.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
         {
         {
             return;
             return;
         }
         }
@@ -42,21 +42,20 @@ public static class CropFunctions
         // Hide bottom gallery when entering crop mode
         // Hide bottom gallery when entering crop mode
         if (isBottomGalleryShown)
         if (isBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.Closed;
+            vm.Gallery.GalleryMode.Value = GalleryMode.Closed;
             // Reset setting before resizing
             // Reset setting before resizing
             Settings.Gallery.IsBottomGalleryShown = false;
             Settings.Gallery.IsBottomGalleryShown = false;
             await WindowResizing.SetSizeAsync(vm);
             await WindowResizing.SetSizeAsync(vm);
         }
         }
 
 
-        var size = new Size(vm.PicViewer.ImageWidth, vm.PicViewer.ImageHeight);
+        var size = new Size(vm.PicViewer.ImageWidth.CurrentValue, vm.PicViewer.ImageHeight.CurrentValue);
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
-            vm.Crop = new ImageCropperViewModel(bitmap)
-            {
-                ImageWidth = size.Width,
-                ImageHeight = size.Height,
-                AspectRatio = vm.PicViewer.AspectRatio
-            };
+            vm.Crop = new ImageCropperViewModel(bitmap);
+            vm.Crop.ImageWidth.Value = size.Width;
+            vm.Crop.ImageHeight.Value = size.Height;
+            vm.Crop.AspectRatio.Value = vm.PicViewer.AspectRatio.CurrentValue;
+            
             var cropControl = new CropControl
             var cropControl = new CropControl
             {
             {
                 DataContext = vm,
                 DataContext = vm,
@@ -64,12 +63,12 @@ public static class CropFunctions
                 Height = size.Height,
                 Height = size.Height,
                 Margin = new Thickness(0)
                 Margin = new Thickness(0)
             };
             };
-            vm.CurrentView = cropControl;
+            vm.MainWindow.CurrentView.Value = cropControl;
         });
         });
 
 
         IsCropping = true;
         IsCropping = true;
-        vm.PicViewer.Title = TranslationManager.Translation.CropMessage;
-        vm.PicViewer.TitleTooltip = TranslationManager.Translation.CropMessage;
+        vm.PicViewer.Title.Value = TranslationManager.Translation.CropMessage;
+        vm.PicViewer.TitleTooltip.Value = TranslationManager.Translation.CropMessage;
 
 
         await FunctionsMapper.CloseMenus();
         await FunctionsMapper.CloseMenus();
 
 
@@ -83,24 +82,28 @@ public static class CropFunctions
     {
     {
         if (Settings.Gallery.IsBottomGalleryShown)
         if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.ClosedToBottom;
+            if (vm.Gallery is {} gallery)
+            {
+                gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
+            }
+            
             WindowResizing.SetSize(vm);
             WindowResizing.SetSize(vm);
         }
         }
 
 
-        vm.CurrentView = vm.ImageViewer;
+        vm.MainWindow.CurrentView.Value = vm.ImageViewer;
         IsCropping = false;
         IsCropping = false;
         TitleManager.SetTitle(vm);
         TitleManager.SetTitle(vm);
 
 
         // Reset image type to fix issue with animated images
         // Reset image type to fix issue with animated images
-        switch (vm.PicViewer.ImageType)
+        switch (vm.PicViewer.ImageType.CurrentValue)
         {
         {
             case ImageType.AnimatedWebp:
             case ImageType.AnimatedWebp:
-                vm.PicViewer.ImageType = ImageType.Bitmap;
-                vm.PicViewer.ImageType = ImageType.AnimatedWebp;
+                vm.PicViewer.ImageType.Value = ImageType.Bitmap;
+                vm.PicViewer.ImageType.Value = ImageType.AnimatedWebp;
                 break;
                 break;
             case ImageType.AnimatedGif:
             case ImageType.AnimatedGif:
-                vm.PicViewer.ImageType = ImageType.Bitmap;
-                vm.PicViewer.ImageType = ImageType.AnimatedGif;
+                vm.PicViewer.ImageType.Value = ImageType.Bitmap;
+                vm.PicViewer.ImageType.Value = ImageType.AnimatedGif;
                 break;
                 break;
         }
         }
 
 
@@ -114,9 +117,9 @@ public static class CropFunctions
             return false;
             return false;
         }
         }
 
 
-        if (vm?.PicViewer.ImageSource is not Bitmap || Settings.ImageScaling.ShowImageSideBySide)
+        if (vm?.PicViewer.ImageSource.CurrentValue is not Bitmap || Settings.ImageScaling.ShowImageSideBySide)
         {
         {
-            vm.ShouldCropBeEnabled = false;
+            vm.GlobalSettings.ShouldCropBeEnabled.Value = false;
             return false;
             return false;
         }
         }
 
 
@@ -125,18 +128,18 @@ public static class CropFunctions
             return false;
             return false;
         }
         }
 
 
-        if (vm.IsEditableTitlebarOpen)
+        if (vm.MainWindow.IsEditableTitlebarOpen.CurrentValue)
         {
         {
             return false;
             return false;
         }
         }
 
 
-        if (vm.RotationAngle is 0 && vm.PicViewer.ScaleX is 1)
+        if (vm.GlobalSettings.RotationAngle.CurrentValue is 0 && vm.PicViewer.ScaleX.CurrentValue is 1)
         {
         {
-            vm.ShouldCropBeEnabled = true;
+            vm.GlobalSettings.ShouldCropBeEnabled.Value = true;
             return true;
             return true;
         }
         }
 
 
-        vm.ShouldCropBeEnabled = false;
+        vm.GlobalSettings.ShouldCropBeEnabled.Value = false;
         return false;
         return false;
     }
     }
 }
 }

+ 4 - 5
src/PicView.Avalonia/Crop/CropKeyboardManager.cs

@@ -1,5 +1,4 @@
-using System.Reactive.Linq;
-using System.Runtime.InteropServices;
+using System.Runtime.InteropServices;
 using Avalonia.Input;
 using Avalonia.Input;
 using PicView.Avalonia.Functions;
 using PicView.Avalonia.Functions;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.Input;
@@ -21,7 +20,7 @@ public class CropKeyboardManager(CropControl control)
         switch (e.Key)
         switch (e.Key)
         {
         {
             case Key.Enter:
             case Key.Enter:
-                await vm.Crop.CropImageCommand.Execute();
+                await vm.Crop.SaveCroppedImageAsync();
                 return;
                 return;
             case Key.Escape:
             case Key.Escape:
                 CropFunctions.CloseCropControl(UIHelper.GetMainView.DataContext as MainViewModel);
                 CropFunctions.CloseCropControl(UIHelper.GetMainView.DataContext as MainViewModel);
@@ -86,11 +85,11 @@ public class CropKeyboardManager(CropControl control)
                 case "Save":
                 case "Save":
                 case "SaveAs":
                 case "SaveAs":
                 case "GalleryClick":
                 case "GalleryClick":
-                    await vm.Crop.CropImageCommand.Execute();
+                    await vm.Crop.SaveCroppedImageAsync();
                     return;
                     return;
                 case "CopyImage":
                 case "CopyImage":
                 case "CopyFile":
                 case "CopyFile":
-                    await vm.Crop.CopyCropImageCommand.Execute();
+                    await vm.Crop.CopyCroppedImageAsync();
                     return;
                     return;
             }
             }
         }
         }

+ 23 - 23
src/PicView.Avalonia/Crop/CropLayoutManager.cs

@@ -17,33 +17,33 @@ public class CropLayoutManager(CropControl control)
         }
         }
 
 
         // Ensure image dimensions are valid before proceeding
         // Ensure image dimensions are valid before proceeding
-        if (vm.PicViewer.ImageWidth <= 0 || vm.PicViewer.ImageHeight <= 0)
+        if (vm.PicViewer.ImageWidth.CurrentValue <= 0 || vm.PicViewer.ImageHeight.CurrentValue <= 0)
         {
         {
             return;
             return;
         }
         }
 
 
         // Set initial width and height for the crop rectangle
         // Set initial width and height for the crop rectangle
-        var pixelWidth = vm.PicViewer.ImageWidth / vm.PicViewer.AspectRatio;
-        var pixelHeight = vm.PicViewer.ImageHeight / vm.PicViewer.AspectRatio;
+        var pixelWidth = vm.PicViewer.ImageWidth.CurrentValue / vm.PicViewer.AspectRatio.CurrentValue;
+        var pixelHeight = vm.PicViewer.ImageHeight.CurrentValue / vm.PicViewer.AspectRatio.CurrentValue;
 
 
         if (pixelWidth >= DefaultSelectionSize * 2 || pixelHeight >= DefaultSelectionSize * 2)
         if (pixelWidth >= DefaultSelectionSize * 2 || pixelHeight >= DefaultSelectionSize * 2)
         {
         {
-            vm.Crop.SelectionWidth = DefaultSelectionSize;
-            vm.Crop.SelectionHeight = DefaultSelectionSize;
+            vm.Crop.SetSelectionWidth(DefaultSelectionSize);
+            vm.Crop.SetSelectionHeight(DefaultSelectionSize);
         }
         }
         else if (pixelWidth <= DefaultSelectionSize || pixelHeight <= DefaultSelectionSize)
         else if (pixelWidth <= DefaultSelectionSize || pixelHeight <= DefaultSelectionSize)
         {
         {
-            vm.Crop.SelectionWidth = pixelWidth / 2;
-            vm.Crop.SelectionHeight = pixelHeight / 2;
+            vm.Crop.SetSelectionWidth((uint)(pixelWidth / 2));
+            vm.Crop.SetSelectionHeight((uint)(pixelHeight / 2));
         }
         }
 
 
         // Calculate centered position
         // Calculate centered position
-        vm.Crop.SelectionX = Convert.ToInt32((vm.PicViewer.ImageWidth - vm.Crop.SelectionWidth) / 2);
-        vm.Crop.SelectionY = Convert.ToInt32((vm.PicViewer.ImageHeight - vm.Crop.SelectionHeight) / 2);
+        vm.Crop.SelectionX.Value = Convert.ToInt32((vm.PicViewer.ImageWidth.CurrentValue - vm.Crop.SelectionWidth.CurrentValue) / 2);
+        vm.Crop.SelectionY.Value = Convert.ToInt32((vm.PicViewer.ImageHeight.CurrentValue - vm.Crop.SelectionHeight.CurrentValue) / 2);
 
 
         // Apply the calculated position to the MainRectangle
         // Apply the calculated position to the MainRectangle
-        Canvas.SetLeft(control.MainRectangle, vm.Crop.SelectionX);
-        Canvas.SetTop(control.MainRectangle, vm.Crop.SelectionY);
+        Canvas.SetLeft(control.MainRectangle, vm.Crop.SelectionX.CurrentValue);
+        Canvas.SetTop(control.MainRectangle, vm.Crop.SelectionY.CurrentValue);
 
 
         UpdateLayout();
         UpdateLayout();
     }
     }
@@ -71,31 +71,31 @@ public class CropLayoutManager(CropControl control)
         // Converting to int fixes black border
         // Converting to int fixes black border
         var left = Convert.ToInt32(Canvas.GetLeft(control.MainRectangle));
         var left = Convert.ToInt32(Canvas.GetLeft(control.MainRectangle));
         var top = Convert.ToInt32(Canvas.GetTop(control.MainRectangle));
         var top = Convert.ToInt32(Canvas.GetTop(control.MainRectangle));
-        var right = Convert.ToInt32(left + vm.Crop.SelectionWidth);
-        var bottom = Convert.ToInt32(top + vm.Crop.SelectionHeight);
+        var right = Convert.ToInt32(left + vm.Crop.SelectionWidth.CurrentValue);
+        var bottom = Convert.ToInt32(top + vm.Crop.SelectionHeight.CurrentValue);
 
 
         // Calculate the positions and sizes for the surrounding rectangles
         // Calculate the positions and sizes for the surrounding rectangles
         // Top Rectangle (above MainRectangle)
         // Top Rectangle (above MainRectangle)
-        control.TopRectangle.Width = vm.PicViewer.ImageWidth;
+        control.TopRectangle.Width = vm.PicViewer.ImageWidth.CurrentValue;
         control.TopRectangle.Height = top < 0 ? 0 : top;
         control.TopRectangle.Height = top < 0 ? 0 : top;
         Canvas.SetTop(control.TopRectangle, 0);
         Canvas.SetTop(control.TopRectangle, 0);
 
 
         // Bottom Rectangle (below MainRectangle)
         // Bottom Rectangle (below MainRectangle)
-        control.BottomRectangle.Width = vm.PicViewer.ImageWidth;
-        var newBottomRectangleHeight = vm.PicViewer.ImageHeight - bottom < 0 ? 0 : vm.PicViewer.ImageHeight - bottom;
+        control.BottomRectangle.Width = vm.PicViewer.ImageWidth.CurrentValue;
+        var newBottomRectangleHeight = vm.PicViewer.ImageHeight.CurrentValue - bottom < 0 ? 0 : vm.PicViewer.ImageHeight.CurrentValue - bottom;
         control.BottomRectangle.Height = newBottomRectangleHeight;
         control.BottomRectangle.Height = newBottomRectangleHeight;
         Canvas.SetTop(control.BottomRectangle, bottom);
         Canvas.SetTop(control.BottomRectangle, bottom);
 
 
         // Left Rectangle (left of MainRectangle)
         // Left Rectangle (left of MainRectangle)
         control.LeftRectangle.Width = left < 0 ? 0 : left;
         control.LeftRectangle.Width = left < 0 ? 0 : left;
-        control.LeftRectangle.Height = vm.Crop.SelectionHeight;
+        control.LeftRectangle.Height = vm.Crop.SelectionHeight.CurrentValue;
         Canvas.SetLeft(control.LeftRectangle, 0);
         Canvas.SetLeft(control.LeftRectangle, 0);
         Canvas.SetTop(control.LeftRectangle, top);
         Canvas.SetTop(control.LeftRectangle, top);
 
 
         // Right Rectangle (right of MainRectangle)
         // Right Rectangle (right of MainRectangle)
-        var newRightRectangleWidth = vm.PicViewer.ImageWidth - right < 0 ? 0 : vm.PicViewer.ImageWidth - right;
+        var newRightRectangleWidth = vm.PicViewer.ImageWidth.CurrentValue - right < 0 ? 0 : vm.PicViewer.ImageWidth.CurrentValue - right;
         control.RightRectangle.Width = newRightRectangleWidth;
         control.RightRectangle.Width = newRightRectangleWidth;
-        control.RightRectangle.Height = vm.Crop.SelectionHeight;
+        control.RightRectangle.Height = vm.Crop.SelectionHeight.CurrentValue;
         Canvas.SetLeft(control.RightRectangle, right);
         Canvas.SetLeft(control.RightRectangle, right);
         Canvas.SetTop(control.RightRectangle, top);
         Canvas.SetTop(control.RightRectangle, top);
     }
     }
@@ -107,10 +107,10 @@ public class CropLayoutManager(CropControl control)
             return;
             return;
         }
         }
 
 
-        var selectionX = vm.Crop.SelectionX;
-        var selectionY = vm.Crop.SelectionY;
-        var selectionWidth = vm.Crop.SelectionWidth;
-        var selectionHeight = vm.Crop.SelectionHeight;
+        var selectionX = vm.Crop.SelectionX.CurrentValue;
+        var selectionY = vm.Crop.SelectionY.CurrentValue;
+        var selectionWidth = vm.Crop.SelectionWidth.CurrentValue;
+        var selectionHeight = vm.Crop.SelectionHeight.CurrentValue;
 
 
         // Get the bounds of the RootCanvas (the control container)
         // Get the bounds of the RootCanvas (the control container)
         const int rootCanvasLeft = 0;
         const int rootCanvasLeft = 0;

+ 2 - 2
src/PicView.Avalonia/Crop/CropResizeHandler.cs

@@ -21,8 +21,8 @@ public class CropResizeHandler(CropControl control)
         }
         }
 
 
         _resizeStart = e.GetPosition(control.RootCanvas);
         _resizeStart = e.GetPosition(control.RootCanvas);
-        _originalRect = new Rect(Canvas.GetLeft(control.MainRectangle), Canvas.GetTop(control.MainRectangle), vm.Crop.SelectionWidth,
-            vm.Crop.SelectionHeight);
+        _originalRect = new Rect(Canvas.GetLeft(control.MainRectangle), Canvas.GetTop(control.MainRectangle), vm.Crop.SelectionWidth.CurrentValue,
+            vm.Crop.SelectionHeight.CurrentValue);
         _isResizing = true;
         _isResizing = true;
     }
     }
 
 

+ 5 - 5
src/PicView.Avalonia/Crop/CropResizer.cs

@@ -96,15 +96,15 @@ public static class CropResizer
         }
         }
         
         
         // Apply bounds constraints
         // Apply bounds constraints
-        ApplyBoundsConstraints(ref newX, ref newY, ref newWidth, ref newHeight, vm.ImageWidth, vm.ImageHeight);
+        ApplyBoundsConstraints(ref newX, ref newY, ref newWidth, ref newHeight, vm.ImageWidth.CurrentValue, vm.ImageHeight.CurrentValue);
         
         
         // Update the view model
         // Update the view model
         try
         try
         {
         {
-            vm.SelectionX = Convert.ToInt32(newX);
-            vm.SelectionY = Convert.ToInt32(newY);
-            vm.SelectionWidth = Convert.ToInt32(newWidth);
-            vm.SelectionHeight = Convert.ToInt32(newHeight);
+            vm.SelectionX.Value = Convert.ToInt32(newX);
+            vm.SelectionY.Value = Convert.ToInt32(newY);
+            vm.SetSelectionWidth(Convert.ToUInt32(newWidth));
+            vm.SetSelectionHeight(Convert.ToUInt32(newHeight));
         }
         }
         catch (Exception exception)
         catch (Exception exception)
         {
         {

+ 4 - 4
src/PicView.Avalonia/CustomControls/AnimatedMenu.cs

@@ -1,8 +1,8 @@
-using System.Reactive.Linq;
-using Avalonia;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using PicView.Avalonia.Animations;
 using PicView.Avalonia.Animations;
-using ReactiveUI;
+using PicView.Avalonia.UI;
+using R3;
 
 
 namespace PicView.Avalonia.CustomControls;
 namespace PicView.Avalonia.CustomControls;
 
 
@@ -20,7 +20,7 @@ public class AnimatedMenu : UserControl
     protected AnimatedMenu()
     protected AnimatedMenu()
     {
     {
         // Subscribe to changes in the IsOpen property
         // Subscribe to changes in the IsOpen property
-        this.WhenAnyValue(x => x.IsOpen)
+        Observable.EveryValueChanged(this, x => x.IsOpen, UIHelper.GetFrameProvider)
             .Select(async isOpen =>
             .Select(async isOpen =>
             {
             {
                 // Make sure it is visible before starting the animation
                 // Make sure it is visible before starting the animation

+ 8 - 9
src/PicView.Avalonia/CustomControls/AutoScrollViewer.cs

@@ -1,7 +1,4 @@
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
-using Avalonia;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Metadata;
 using Avalonia.Controls.Primitives;
 using Avalonia.Controls.Primitives;
@@ -9,7 +6,9 @@ using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Media;
 using Avalonia.Media;
 using Avalonia.VisualTree;
 using Avalonia.VisualTree;
-using ReactiveUI;
+using PicView.Avalonia.UI;
+using R3;
+using CompositeDisposable = R3.CompositeDisposable;
 
 
 namespace PicView.Avalonia.CustomControls;
 namespace PicView.Avalonia.CustomControls;
 
 
@@ -101,7 +100,7 @@ public class AutoScrollViewer : ScrollViewer
         var autoScrollSign = e.NameScope.Find<AutoScrollSign>("PART_AutoScrollSign");
         var autoScrollSign = e.NameScope.Find<AutoScrollSign>("PART_AutoScrollSign");
 
 
         _autoScrollingSubject
         _autoScrollingSubject
-            .ObserveOn(RxApp.MainThreadScheduler)
+            .ObserveOn(UIHelper.GetFrameProvider)
             .Subscribe(isAutoScrolling =>
             .Subscribe(isAutoScrolling =>
             {
             {
                 var canScroll = CanScroll();
                 var canScroll = CanScroll();
@@ -125,7 +124,7 @@ public class AutoScrollViewer : ScrollViewer
                         break;
                         break;
                 }
                 }
             })
             })
-            .DisposeWith(_disposables);
+            .AddTo(_disposables);
 
 
         // Handle all types of focus loss events to end auto-scrolling
         // Handle all types of focus loss events to end auto-scrolling
         LostFocus += (_, _) => IsAutoScrolling = false;
         LostFocus += (_, _) => IsAutoScrolling = false;
@@ -180,9 +179,9 @@ public class AutoScrollViewer : ScrollViewer
 
 
         Observable.Interval(TimeSpan.FromMilliseconds(16))
         Observable.Interval(TimeSpan.FromMilliseconds(16))
             .TakeUntil(_autoScrollingSubject.Where(isScrolling => !isScrolling))
             .TakeUntil(_autoScrollingSubject.Where(isScrolling => !isScrolling))
-            .ObserveOn(RxApp.MainThreadScheduler)
+            .ObserveOn(UIHelper.GetFrameProvider)
             .Subscribe(_ => PerformAutoScroll())
             .Subscribe(_ => PerformAutoScroll())
-            .DisposeWith(_disposables);
+            .AddTo(_disposables);
     }
     }
 
 
     /// <summary>
     /// <summary>

+ 46 - 47
src/PicView.Avalonia/CustomControls/GalleryAnimationControl.cs

@@ -1,5 +1,4 @@
-using System.Reactive.Linq;
-using Avalonia;
+using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Input;
 using Avalonia.Input;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
@@ -13,12 +12,29 @@ using PicView.Avalonia.WindowBehavior;
 using PicView.Core.DebugTools;
 using PicView.Core.DebugTools;
 using PicView.Core.Gallery;
 using PicView.Core.Gallery;
 using PicView.Core.Sizing;
 using PicView.Core.Sizing;
-using ReactiveUI;
+using R3;
 
 
 namespace PicView.Avalonia.CustomControls;
 namespace PicView.Avalonia.CustomControls;
 
 
 public class GalleryAnimationControl : UserControl
 public class GalleryAnimationControl : UserControl
 {
 {
+    #region Cleanup
+
+    protected override void OnUnloaded(RoutedEventArgs e)
+    {
+        base.OnUnloaded(e);
+
+        if (Parent is Control parent)
+        {
+            parent.SizeChanged -= ParentSizeChanged;
+        }
+
+        Loaded -= OnControlLoaded;
+        RemoveHandler(PointerPressedEvent, PreviewPointerPressedEvent);
+    }
+
+    #endregion
+
     #region Fields and Properties
     #region Fields and Properties
 
 
     private const double FastAnimationSpeed = 0.3;
     private const double FastAnimationSpeed = 0.3;
@@ -56,9 +72,8 @@ public class GalleryAnimationControl : UserControl
     {
     {
         AddHandler(PointerPressedEvent, PreviewPointerPressedEvent, RoutingStrategies.Tunnel);
         AddHandler(PointerPressedEvent, PreviewPointerPressedEvent, RoutingStrategies.Tunnel);
 
 
-        this.WhenAnyValue(x => x.GalleryMode)
-            .WhereNotNull()
-            .SelectMany(async galleryMode =>
+        Observable.EveryValueChanged(this, x => x.GalleryMode, UIHelper.GetFrameProvider)
+            .SelectAwait(async (galleryMode, _) =>
             {
             {
                 try
                 try
                 {
                 {
@@ -134,7 +149,8 @@ public class GalleryAnimationControl : UserControl
             IsVisible = true;
             IsVisible = true;
             Opacity = FullOpacity;
             Opacity = FullOpacity;
             Height = double.NaN;
             Height = double.NaN;
-            ViewModel.GalleryOrientation = Orientation.Horizontal;
+            ViewModel.Gallery.GalleryOrientation.Value = Orientation.Horizontal;
+            ViewModel.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Bottom;
         });
         });
     }
     }
 
 
@@ -158,13 +174,13 @@ public class GalleryAnimationControl : UserControl
                 Opacity = NoOpacity;
                 Opacity = NoOpacity;
                 Height = parent.Bounds.Height;
                 Height = parent.Bounds.Height;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = true;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = true;
-                ViewModel.GalleryItemMargin = FullGalleryItemMargin;
+                ViewModel.Gallery.GalleryItem.ItemMargin.Value = FullGalleryItemMargin;
             });
             });
 
 
             // Configure gallery
             // Configure gallery
-            ViewModel.GalleryOrientation = Orientation.Vertical;
+            ViewModel.Gallery.GalleryOrientation.Value = Orientation.Vertical;
             GalleryStretchMode.DetermineStretchMode(ViewModel);
             GalleryStretchMode.DetermineStretchMode(ViewModel);
-            ViewModel.IsFullGalleryOpen = true;
+            ViewModel.Gallery.IsGalleryExpanded.Value = true;
 
 
             // Animate opacity
             // Animate opacity
             var opacityAnimation = AnimationsHelper.OpacityAnimation(NoOpacity, FullOpacity, MediumAnimationSpeed);
             var opacityAnimation = AnimationsHelper.OpacityAnimation(NoOpacity, FullOpacity, MediumAnimationSpeed);
@@ -174,7 +190,7 @@ public class GalleryAnimationControl : UserControl
             await Dispatcher.UIThread.InvokeAsync(() =>
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
             {
                 Opacity = FullOpacity;
                 Opacity = FullOpacity;
-                ViewModel.GalleryVerticalAlignment = VerticalAlignment.Stretch;
+                ViewModel.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Stretch;
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
             });
             });
         }
         }
@@ -204,7 +220,7 @@ public class GalleryAnimationControl : UserControl
 
 
             // Animate opacity
             // Animate opacity
             var opacityAnimation = AnimationsHelper.OpacityAnimation(FullOpacity, NoOpacity, FastAnimationSpeed);
             var opacityAnimation = AnimationsHelper.OpacityAnimation(FullOpacity, NoOpacity, FastAnimationSpeed);
-            ViewModel.GalleryMargin = new Thickness(0);
+            ViewModel.Gallery.GalleryMargin.Value = new Thickness(0);
             await opacityAnimation.RunAsync(this);
             await opacityAnimation.RunAsync(this);
 
 
             // Apply final state
             // Apply final state
@@ -243,17 +259,17 @@ public class GalleryAnimationControl : UserControl
                 Opacity = FullOpacity;
                 Opacity = FullOpacity;
                 WindowResizing.SetSize(ViewModel);
                 WindowResizing.SetSize(ViewModel);
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = false;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = false;
-                ViewModel.GalleryItemMargin = BottomGalleryItemMargin;
+                ViewModel.Gallery.GalleryItem.ItemMargin.Value = BottomGalleryItemMargin;
             });
             });
 
 
             // Configure gallery
             // Configure gallery
-            ViewModel.GalleryOrientation = Orientation.Horizontal;
+            ViewModel. Gallery.GalleryOrientation.Value = Orientation.Horizontal;
             GalleryStretchMode.DetermineStretchMode(ViewModel);
             GalleryStretchMode.DetermineStretchMode(ViewModel);
-            ViewModel.IsFullGalleryOpen = false;
-            ViewModel.GalleryVerticalAlignment = VerticalAlignment.Bottom;
+            ViewModel.Gallery.IsGalleryExpanded.Value = false;
+            ViewModel.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Bottom;
 
 
             // Animate height
             // Animate height
-            var to = ViewModel.GalleryHeight;
+            var to = GalleryFunctions.GetGalleryHeight(ViewModel);
             var heightAnimation = AnimationsHelper.HeightAnimation(ZeroHeight, to, FastAnimationSpeed);
             var heightAnimation = AnimationsHelper.HeightAnimation(ZeroHeight, to, FastAnimationSpeed);
             await heightAnimation.RunAsync(this);
             await heightAnimation.RunAsync(this);
 
 
@@ -283,7 +299,7 @@ public class GalleryAnimationControl : UserControl
             _isAnimating = true;
             _isAnimating = true;
 
 
             // Animate closing
             // Animate closing
-            var from = ViewModel.GetBottomGalleryItemHeight + SizeDefaults.ScrollbarSize;
+            var from = ViewModel.Gallery.GalleryItem.BottomGalleryItemHeight.Value + SizeDefaults.ScrollbarSize;
             await Dispatcher.UIThread.InvokeAsync(() =>
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
             {
                 Height = from;
                 Height = from;
@@ -293,8 +309,8 @@ public class GalleryAnimationControl : UserControl
             });
             });
 
 
             // Configure gallery
             // Configure gallery
-            ViewModel.GalleryOrientation = Orientation.Horizontal;
-            ViewModel.IsFullGalleryOpen = false;
+            ViewModel.Gallery.GalleryOrientation.Value = Orientation.Horizontal;
+            ViewModel.Gallery.IsGalleryExpanded.Value = false;
 
 
             // Animate height
             // Animate height
             var heightAnimation = AnimationsHelper.HeightAnimation(from, ZeroHeight, FastAnimationSpeed);
             var heightAnimation = AnimationsHelper.HeightAnimation(from, ZeroHeight, FastAnimationSpeed);
@@ -326,13 +342,13 @@ public class GalleryAnimationControl : UserControl
             _isAnimating = true;
             _isAnimating = true;
 
 
             // Configure gallery
             // Configure gallery
-            ViewModel.GalleryOrientation = Orientation.Vertical;
-            ViewModel.IsFullGalleryOpen = true;
+            ViewModel.Gallery.GalleryOrientation.Value = Orientation.Vertical;
+            ViewModel.Gallery.IsGalleryExpanded.Value = true;
             GalleryStretchMode.DetermineStretchMode(ViewModel);
             GalleryStretchMode.DetermineStretchMode(ViewModel);
-            ViewModel.GalleryItemMargin = FullGalleryItemMargin;
+            ViewModel.Gallery.GalleryItem.ItemMargin.Value = FullGalleryItemMargin;
 
 
             // Animate height
             // Animate height
-            var from = ViewModel.GalleryHeight;
+            var from = GalleryFunctions.GetGalleryHeight(ViewModel);
             var to = parent.Bounds.Height;
             var to = parent.Bounds.Height;
             var heightAnimation = AnimationsHelper.HeightAnimation(from, to, MediumAnimationSpeed);
             var heightAnimation = AnimationsHelper.HeightAnimation(from, to, MediumAnimationSpeed);
             await heightAnimation.RunAsync(this);
             await heightAnimation.RunAsync(this);
@@ -342,7 +358,7 @@ public class GalleryAnimationControl : UserControl
             {
             {
                 Height = to;
                 Height = to;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = true;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = true;
-                ViewModel.GalleryVerticalAlignment = VerticalAlignment.Stretch;
+                ViewModel.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Stretch;
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
             });
             });
         }
         }
@@ -364,12 +380,12 @@ public class GalleryAnimationControl : UserControl
             _isAnimating = true;
             _isAnimating = true;
 
 
             // Configure gallery
             // Configure gallery
-            ViewModel.GalleryVerticalAlignment = VerticalAlignment.Bottom;
-            ViewModel.IsFullGalleryOpen = false;
+            ViewModel.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Bottom;
+            ViewModel.Gallery.IsGalleryExpanded.Value = false;
 
 
             // Animate height
             // Animate height
             var from = Bounds.Height;
             var from = Bounds.Height;
-            var to = ViewModel.GalleryHeight;
+            var to = GalleryFunctions.GetGalleryHeight(ViewModel);
             var heightAnimation = AnimationsHelper.HeightAnimation(from, to, SlowAnimationSpeed);
             var heightAnimation = AnimationsHelper.HeightAnimation(from, to, SlowAnimationSpeed);
             await heightAnimation.RunAsync(this);
             await heightAnimation.RunAsync(this);
 
 
@@ -383,8 +399,8 @@ public class GalleryAnimationControl : UserControl
             {
             {
                 Height = parent.Bounds.Height;
                 Height = parent.Bounds.Height;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = false;
                 UIHelper.GetGalleryView.BlurMask.BlurEnabled = false;
-                ViewModel.GalleryItemMargin = BottomGalleryItemMargin;
-                ViewModel.GalleryOrientation = Orientation.Horizontal;
+                ViewModel.Gallery.GalleryItem.ItemMargin.Value = BottomGalleryItemMargin;
+                ViewModel.Gallery.GalleryOrientation.Value = Orientation.Horizontal;
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
                 GalleryNavigation.CenterScrollToSelectedItem(ViewModel);
             });
             });
         }
         }
@@ -421,21 +437,4 @@ public class GalleryAnimationControl : UserControl
     }
     }
 
 
     #endregion
     #endregion
-    
-    #region Cleanup
-
-    protected override void OnUnloaded(RoutedEventArgs e)
-    {
-        base.OnUnloaded(e);
-
-        if (Parent is Control parent)
-        {
-            parent.SizeChanged -= ParentSizeChanged;
-        }
-
-        Loaded -= OnControlLoaded;
-        RemoveHandler(PointerPressedEvent, PreviewPointerPressedEvent);
-    }
-
-    #endregion
 }
 }

+ 2 - 1
src/PicView.Avalonia/CustomControls/KeybindTextBox.cs

@@ -9,6 +9,7 @@ using Avalonia.Threading;
 using PicView.Avalonia.Functions;
 using PicView.Avalonia.Functions;
 using PicView.Avalonia.Input;
 using PicView.Avalonia.Input;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using R3;
 
 
 namespace PicView.Avalonia.CustomControls;
 namespace PicView.Avalonia.CustomControls;
 
 
@@ -45,7 +46,7 @@ public class KeybindTextBox : TextBox
 
 
     public KeybindTextBox()
     public KeybindTextBox()
     {
     {
-        this.GetObservable(MethodNameProperty).Subscribe(_ => Text = GetFunctionKey());
+        this.GetObservable(MethodNameProperty).ToObservable().Subscribe(_ => Text = GetFunctionKey());
         if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         {
         {
             AddHandler(KeyUpEvent, KeyDownHandler, RoutingStrategies.Tunnel);
             AddHandler(KeyUpEvent, KeyDownHandler, RoutingStrategies.Tunnel);

+ 8 - 10
src/PicView.Avalonia/CustomControls/PicBox.cs

@@ -16,7 +16,8 @@ using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Core.DebugTools;
 using PicView.Core.DebugTools;
 using PicView.Core.ImageDecoding;
 using PicView.Core.ImageDecoding;
-using ReactiveUI;
+using R3;
+using CompositeDisposable = R3.CompositeDisposable;
 using Vector = Avalonia.Vector;
 using Vector = Avalonia.Vector;
 
 
 namespace PicView.Avalonia.CustomControls;
 namespace PicView.Avalonia.CustomControls;
@@ -46,7 +47,7 @@ public class PicBox : Control, IDisposable
     private FileStream? _stream;
     private FileStream? _stream;
     private IGifInstance? _animInstance;
     private IGifInstance? _animInstance;
     public string? InitialAnimatedSource;
     public string? InitialAnimatedSource;
-    private readonly IDisposable? _imageTypeSubscription;
+    private readonly CompositeDisposable _imageTypeSubscription = new();
     private bool _isDisposed;
     private bool _isDisposed;
 
 
     public static readonly StyledProperty<object?> SourceProperty =
     public static readonly StyledProperty<object?> SourceProperty =
@@ -110,11 +111,9 @@ public class PicBox : Control, IDisposable
         AffectsRender<PicBox>(SourceProperty);
         AffectsRender<PicBox>(SourceProperty);
     }
     }
 
 
-    public PicBox()
-    {
-        _imageTypeSubscription = this.WhenAnyValue(x => x.ImageType)
-            .Subscribe(UpdateSource);
-    }
+    public PicBox() =>
+        Observable.EveryValueChanged(this, x => x.ImageType, UIHelper.GetFrameProvider)
+            .Subscribe(UpdateSource).AddTo(_imageTypeSubscription);
 
 
     #endregion
     #endregion
 
 
@@ -282,7 +281,7 @@ public class PicBox : Control, IDisposable
             return new Size(preloadValue.ImageModel.PixelWidth, preloadValue.ImageModel.PixelHeight);
             return new Size(preloadValue.ImageModel.PixelWidth, preloadValue.ImageModel.PixelHeight);
         }
         }
 
 
-        if (vm.PicViewer.FileInfo?.Exists != true)
+        if (vm.PicViewer.FileInfo?.CurrentValue.Exists != true)
         {
         {
             return new Size();
             return new Size();
         }
         }
@@ -290,7 +289,7 @@ public class PicBox : Control, IDisposable
         try
         try
         {
         {
             using var magickImage = new MagickImage();
             using var magickImage = new MagickImage();
-            magickImage.Ping(vm.PicViewer.FileInfo);
+            magickImage.Ping(vm.PicViewer.FileInfo.CurrentValue);
             return new Size(magickImage.Width, magickImage.Height);
             return new Size(magickImage.Width, magickImage.Height);
         }
         }
         catch (Exception exception)
         catch (Exception exception)
@@ -554,7 +553,6 @@ public class PicBox : Control, IDisposable
             return;
             return;
         }
         }
 
 
-        _imageTypeSubscription?.Dispose();
         _animInstance?.Dispose();
         _animInstance?.Dispose();
         _stream?.Dispose();
         _stream?.Dispose();
         DestroyVisual();
         DestroyVisual();

+ 3 - 3
src/PicView.Avalonia/DragAndDrop/DragAndDropHelper.cs

@@ -142,7 +142,7 @@ public static class DragAndDropHelper
     {
     {
         // Remove preview first and show loading
         // Remove preview first and show loading
         RemoveDragDropView();
         RemoveDragDropView();
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         if (vm.ImageViewer?.MainImage != null)
         if (vm.ImageViewer?.MainImage != null)
         {
         {
             vm.ImageViewer.MainImage.Source = null;
             vm.ImageViewer.MainImage.Source = null;
@@ -376,9 +376,9 @@ public static class DragAndDropHelper
 
 
     private static async Task EnsureImageViewerDisplayed(MainViewModel vm)
     private static async Task EnsureImageViewerDisplayed(MainViewModel vm)
     {
     {
-        if (vm.CurrentView != vm.ImageViewer)
+        if (vm.MainWindow.CurrentView.CurrentValue != vm.ImageViewer)
         {
         {
-            await Dispatcher.UIThread.InvokeAsync(() => vm.CurrentView = vm.ImageViewer);
+            await Dispatcher.UIThread.InvokeAsync(() => vm.MainWindow.CurrentView.Value = vm.ImageViewer);
         }
         }
     }
     }
 
 

+ 2 - 2
src/PicView.Avalonia/FileSystem/FileManager.cs

@@ -95,7 +95,7 @@ public static class FileManager
 
 
         try
         try
         {
         {
-            vm.IsLoading = true;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = true;
             var file = await ImageFormatConverter.ConvertToCommonSupportedFormatAsync(path, vm)
             var file = await ImageFormatConverter.ConvertToCommonSupportedFormatAsync(path, vm)
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
 
 
@@ -113,7 +113,7 @@ public static class FileManager
         }
         }
         finally
         finally
         {
         {
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         }
         }
     }
     }
 
 

+ 1 - 1
src/PicView.Avalonia/FileSystem/FileRenamer.cs

@@ -12,7 +12,7 @@ public static class FileRenamer
 
 
     public static async Task<bool> AttemptRenameAsync(string oldPath, string newPath, MainViewModel vm, uint? width = null, uint? height = null, uint? quality = null)
     public static async Task<bool> AttemptRenameAsync(string oldPath, string newPath, MainViewModel vm, uint? width = null, uint? height = null, uint? quality = null)
     {
     {
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
 
 
         if (Path.GetExtension(newPath) != Path.GetExtension(oldPath))
         if (Path.GetExtension(newPath) != Path.GetExtension(oldPath))
         {
         {

+ 6 - 7
src/PicView.Avalonia/FileSystem/FileSaverHelper.cs

@@ -1,5 +1,4 @@
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
-using PicView.Avalonia.ImageHandling;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Core.FileHandling;
 using PicView.Core.FileHandling;
 using PicView.Core.ImageDecoding;
 using PicView.Core.ImageDecoding;
@@ -21,7 +20,7 @@ public static class FileSaverHelper
         }
         }
         else
         else
         {
         {
-            await SaveFileAsync(vm.PicViewer.FileInfo.FullName, vm.PicViewer.FileInfo.FullName, vm);
+            await SaveFileAsync(vm.PicViewer.FileInfo.CurrentValue.FullName, vm.PicViewer.FileInfo.CurrentValue.FullName, vm);
         }
         }
         
         
         //TODO: Add visual design to tell the user that file was saved
         //TODO: Add visual design to tell the user that file was saved
@@ -35,7 +34,7 @@ public static class FileSaverHelper
         }
         }
         
         
         // Suggest random filename for saving, if it is not an existing file
         // Suggest random filename for saving, if it is not an existing file
-        var fileName = vm.PicViewer.FileInfo is null ? Path.GetRandomFileName() : vm.PicViewer.FileInfo.Name;
+        var fileName = vm.PicViewer.FileInfo is null ? Path.GetRandomFileName() : vm.PicViewer.FileInfo.CurrentValue.Name;
 
 
         await FilePicker.PickAndSaveFileAsAsync(fileName, vm);
         await FilePicker.PickAndSaveFileAsAsync(fileName, vm);
     }
     }
@@ -66,7 +65,7 @@ public static class FileSaverHelper
                 null,
                 null,
                 null,
                 null,
                 Path.GetExtension(destination),
                 Path.GetExtension(destination),
-                vm.RotationAngle);
+                vm.GlobalSettings.RotationAngle.CurrentValue);
         }
         }
         
         
         async Task SaveImageFromBitmap()
         async Task SaveImageFromBitmap()
@@ -74,13 +73,13 @@ public static class FileSaverHelper
             
             
             try
             try
             {
             {
-                switch (vm.PicViewer.ImageType)
+                switch (vm.PicViewer.ImageType.CurrentValue)
                 {
                 {
                     case ImageType.AnimatedGif: // TODO: Add animated GIF support
                     case ImageType.AnimatedGif: // TODO: Add animated GIF support
                     case ImageType.AnimatedWebp: // TODO: Add animated WebP support
                     case ImageType.AnimatedWebp: // TODO: Add animated WebP support
                     case ImageType.Bitmap:
                     case ImageType.Bitmap:
                     {
                     {
-                        if (vm.PicViewer.ImageSource is not Bitmap bitmap)
+                        if (vm.PicViewer.ImageSource.CurrentValue is not Bitmap bitmap)
                         {
                         {
                             throw new InvalidOperationException("No bitmap available for saving.");
                             throw new InvalidOperationException("No bitmap available for saving.");
                         }
                         }
@@ -100,7 +99,7 @@ public static class FileSaverHelper
                                 height: null,
                                 height: null,
                                 quality,
                                 quality,
                                 ext,
                                 ext,
-                                vm.RotationAngle);
+                                vm.GlobalSettings.RotationAngle.CurrentValue);
                         }
                         }
                     
                     
                         break;
                         break;

+ 0 - 99
src/PicView.Avalonia/Functions/FunctionsHelper.cs

@@ -1,99 +0,0 @@
-using System.Diagnostics;
-using System.Reactive;
-using PicView.Avalonia.UI;
-using ReactiveUI;
-
-namespace PicView.Avalonia.Functions;
-
-public static class FunctionsHelper
-{
-    /// <summary>
-    ///     Creates a ReactiveCommand from a Task with built-in error handling.
-    /// </summary>
-    /// <param name="task">The task to execute when the command is invoked.</param>
-    /// <param name="canExecute">An optional observable determining when the command can execute.</param>
-    /// <returns>A ReactiveCommand with configured error handling.</returns>
-    public static ReactiveCommand<Unit, Unit> CreateReactiveCommand(
-        Func<Task> task,
-        IObservable<bool>? canExecute = null)
-    {
-        var cmd = ReactiveCommand.CreateFromTask(task, canExecute);
-
-        cmd.ThrownExceptions
-            .Subscribe(ex =>
-            {
-                _ = TooltipHelper.ShowTooltipMessageAsync(ex.Message);
-                Debug.WriteLine($"Error in command: {ex}");
-            });
-
-        return cmd;
-    }
-
-    /// <summary>
-    ///     Creates a parameterized ReactiveCommand from a Task with built-in error handling.
-    /// </summary>
-    /// <typeparam name="TParam">The type of the parameter passed to the task.</typeparam>
-    /// <param name="task">The task to execute when the command is invoked, accepting a parameter.</param>
-    /// <param name="canExecute">An optional observable determining when the command can execute.</param>
-    /// <returns>A ReactiveCommand with configured error handling that accepts a parameter.</returns>
-    public static ReactiveCommand<TParam, Unit> CreateReactiveCommand<TParam>(
-        Func<TParam, Task> task,
-        IObservable<bool>? canExecute = null)
-    {
-        var cmd = ReactiveCommand.CreateFromTask(task, canExecute);
-
-        cmd.ThrownExceptions
-            .Subscribe(ex =>
-            {
-                _ = TooltipHelper.ShowTooltipMessageAsync(ex.Message);
-                Debug.WriteLine($"Error in command: {ex}");
-            });
-
-        return cmd;
-    }
-
-    /// <summary>
-    ///     Creates a ReactiveCommand from a synchronous action with built-in error handling.
-    /// </summary>
-    /// <param name="execute">The action to execute when the command is invoked.</param>
-    /// <param name="canExecute">An optional observable determining when the command can execute.</param>
-    /// <returns>A ReactiveCommand with configured error handling.</returns>
-    public static ReactiveCommand<Unit, Unit> CreateReactiveCommand(
-        Action execute,
-        IObservable<bool>? canExecute = null)
-    {
-        var cmd = ReactiveCommand.Create(execute, canExecute);
-
-        cmd.ThrownExceptions
-            .Subscribe(ex =>
-            {
-                _ = TooltipHelper.ShowTooltipMessageAsync(ex.Message);
-                Debug.WriteLine($"Error in command: {ex}");
-            });
-
-        return cmd;
-    }
-
-    /// <summary>
-    ///     Creates a parameterized ReactiveCommand from a synchronous action with built-in error handling.
-    /// </summary>
-    /// <typeparam name="TParam">The type of the parameter passed to the action.</typeparam>
-    /// <param name="execute">The action to execute when the command is invoked, accepting a parameter.</param>
-    /// <param name="canExecute">An optional observable determining when the command can execute.</param>
-    /// <returns>A ReactiveCommand with configured error handling that accepts a parameter.</returns>
-    public static ReactiveCommand<TParam, Unit> CreateReactiveCommand<TParam>(
-        Action<TParam> execute,
-        IObservable<bool>? canExecute = null)
-    {
-        var cmd = ReactiveCommand.Create(execute, canExecute);
-
-        cmd.ThrownExceptions
-            .Subscribe(ex =>
-            {
-                _ = TooltipHelper.ShowTooltipMessageAsync(ex.Message);
-                Debug.WriteLine($"Error in command: {ex}");
-            });
-
-        return cmd;
-    }
-}

+ 53 - 39
src/PicView.Avalonia/Functions/FunctionsMapper.cs

@@ -536,11 +536,11 @@ public static class FunctionsMapper
 
 
     /// <inheritdoc cref="FileManager.OpenWith(string, MainViewModel)" />
     /// <inheritdoc cref="FileManager.OpenWith(string, MainViewModel)" />
     public static async Task OpenWith() =>
     public static async Task OpenWith() =>
-        await Task.Run(() => Vm?.PlatformService?.OpenWith(Vm.PicViewer.FileInfo?.FullName)).ConfigureAwait(false);
+        await Task.Run(() => Vm?.PlatformService?.OpenWith(Vm.PicViewer.FileInfo?.CurrentValue.FullName)).ConfigureAwait(false);
     
     
     /// <inheritdoc cref="FileManager.LocateOnDisk(string, MainViewModel)" />
     /// <inheritdoc cref="FileManager.LocateOnDisk(string, MainViewModel)" />
     public static async Task OpenInExplorer()=>
     public static async Task OpenInExplorer()=>
-        await Task.Run(() => Vm?.PlatformService?.LocateOnDisk(Vm.PicViewer.FileInfo?.FullName)).ConfigureAwait(false);
+        await Task.Run(() => Vm?.PlatformService?.LocateOnDisk(Vm.PicViewer.FileInfo?.CurrentValue.FullName)).ConfigureAwait(false);
 
 
     /// <inheritdoc cref="FileSaverHelper.SaveCurrentFile(MainViewModel)" />
     /// <inheritdoc cref="FileSaverHelper.SaveCurrentFile(MainViewModel)" />
     public static async Task Save() =>
     public static async Task Save() =>
@@ -552,11 +552,11 @@ public static class FunctionsMapper
     
     
     /// <inheritdoc cref="FileManager.DeleteFileWithOptionalDialog" />
     /// <inheritdoc cref="FileManager.DeleteFileWithOptionalDialog" />
     public static async Task DeleteFile() =>
     public static async Task DeleteFile() =>
-        await FileManager.DeleteFileWithOptionalDialog(true, Vm.PicViewer?.FileInfo?.FullName, Vm.PlatformService).ConfigureAwait(false);
+        await FileManager.DeleteFileWithOptionalDialog(true, Vm.PicViewer?.FileInfo?.CurrentValue.FullName, Vm.PlatformService).ConfigureAwait(false);
     
     
     /// <inheritdoc cref="FileManager.DeleteFileWithOptionalDialog" />
     /// <inheritdoc cref="FileManager.DeleteFileWithOptionalDialog" />
     public static async Task DeleteFilePermanently() =>
     public static async Task DeleteFilePermanently() =>
-        await FileManager.DeleteFileWithOptionalDialog(false, Vm.PicViewer?.FileInfo?.FullName, Vm.PlatformService).ConfigureAwait(false);
+        await FileManager.DeleteFileWithOptionalDialog(false, Vm.PicViewer?.FileInfo?.CurrentValue.FullName, Vm.PlatformService).ConfigureAwait(false);
 
 
     public static async Task Rename()
     public static async Task Rename()
     {
     {
@@ -569,7 +569,7 @@ public static class FunctionsMapper
     
     
     /// <inheritdoc cref="FileManager.ShowFileProperties(string, MainViewModel)" />
     /// <inheritdoc cref="FileManager.ShowFileProperties(string, MainViewModel)" />
     public static async Task ShowFileProperties() =>
     public static async Task ShowFileProperties() =>
-        await Task.Run(() => Vm?.PlatformService?.ShowFileProperties(Vm.PicViewer.FileInfo?.FullName)).ConfigureAwait(false);
+        await Task.Run(() => Vm?.PlatformService?.ShowFileProperties(Vm.PicViewer.FileInfo?.CurrentValue.FullName)).ConfigureAwait(false);
     
     
     #endregion
     #endregion
 
 
@@ -577,11 +577,11 @@ public static class FunctionsMapper
 
 
     /// <inheritdoc cref="ClipboardFileOperations.CopyFileToClipboard(string, MainViewModel)" />
     /// <inheritdoc cref="ClipboardFileOperations.CopyFileToClipboard(string, MainViewModel)" />
     public static async Task CopyFile() =>
     public static async Task CopyFile() =>
-        await ClipboardFileOperations.CopyFileToClipboard(Vm?.PicViewer.FileInfo?.FullName, Vm).ConfigureAwait(false);
+        await ClipboardFileOperations.CopyFileToClipboard(Vm?.PicViewer.FileInfo?.CurrentValue.FullName, Vm).ConfigureAwait(false);
     
     
     /// <inheritdoc cref="ClipboardTextOperations.CopyTextToClipboard(string)" />
     /// <inheritdoc cref="ClipboardTextOperations.CopyTextToClipboard(string)" />
     public static async Task CopyFilePath() => 
     public static async Task CopyFilePath() => 
-        await ClipboardTextOperations.CopyTextToClipboard(Vm?.PicViewer.FileInfo?.FullName).ConfigureAwait(false);
+        await ClipboardTextOperations.CopyTextToClipboard(Vm?.PicViewer.FileInfo?.CurrentValue.FullName).ConfigureAwait(false);
 
 
     /// <inheritdoc cref="ClipboardImageOperations.CopyImageToClipboard(MainViewModel)" />
     /// <inheritdoc cref="ClipboardImageOperations.CopyImageToClipboard(MainViewModel)" />
     public static async Task CopyImage() => 
     public static async Task CopyImage() => 
@@ -589,15 +589,15 @@ public static class FunctionsMapper
 
 
     /// <inheritdoc cref="ClipboardImageOperations.CopyBase64ToClipboard(string, MainViewModel)" />
     /// <inheritdoc cref="ClipboardImageOperations.CopyBase64ToClipboard(string, MainViewModel)" />
     public static async Task CopyBase64() =>
     public static async Task CopyBase64() =>
-        await ClipboardImageOperations.CopyBase64ToClipboard(Vm.PicViewer.FileInfo?.FullName, vm: Vm).ConfigureAwait(false);
+        await ClipboardImageOperations.CopyBase64ToClipboard(Vm.PicViewer.FileInfo?.CurrentValue.FullName, vm: Vm).ConfigureAwait(false);
 
 
     /// <inheritdoc cref="ClipboardFileOperations.Duplicate(string, MainViewModel)" />
     /// <inheritdoc cref="ClipboardFileOperations.Duplicate(string, MainViewModel)" />
     public static async Task DuplicateFile() => 
     public static async Task DuplicateFile() => 
-        await ClipboardFileOperations.Duplicate(Vm.PicViewer.FileInfo?.FullName, Vm).ConfigureAwait(false);
+        await ClipboardFileOperations.Duplicate(Vm.PicViewer.FileInfo?.CurrentValue.FullName, Vm).ConfigureAwait(false);
 
 
     /// <inheritdoc cref="ClipboardFileOperations.CutFile(string, MainViewModel)" />
     /// <inheritdoc cref="ClipboardFileOperations.CutFile(string, MainViewModel)" />
     public static async Task CutFile() =>
     public static async Task CutFile() =>
-        await ClipboardFileOperations.CutFile(Vm.PicViewer.FileInfo.FullName, Vm).ConfigureAwait(false);
+        await ClipboardFileOperations.CutFile(Vm.PicViewer.FileInfo.CurrentValue.FullName, Vm).ConfigureAwait(false);
 
 
     /// <inheritdoc cref="ClipboardPasteOperations.Paste(MainViewModel)" />
     /// <inheritdoc cref="ClipboardPasteOperations.Paste(MainViewModel)" />
     public static async Task Paste() =>
     public static async Task Paste() =>
@@ -697,8 +697,11 @@ public static class FunctionsMapper
             return;
             return;
         }
         }
 
 
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 0); });
-        Vm.EXIFRating = 0;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 0); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 0;
+        }
     }
     }
 
 
     public static async Task Set1Star()
     public static async Task Set1Star()
@@ -709,8 +712,11 @@ public static class FunctionsMapper
             return;
             return;
         }
         }
 
 
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 1); });
-        Vm.EXIFRating = 1;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 1); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 1;
+        }
     }
     }
 
 
     public static async Task Set2Star()
     public static async Task Set2Star()
@@ -720,8 +726,11 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 2); });
-        Vm.EXIFRating = 2;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 2); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 2;
+        }
     }
     }
 
 
     public static async Task Set3Star()
     public static async Task Set3Star()
@@ -731,8 +740,11 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 3); });
-        Vm.EXIFRating = 3;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 3); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 3;
+        }
     }
     }
 
 
     public static async Task Set4Star()
     public static async Task Set4Star()
@@ -742,8 +754,11 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 4); });
-        Vm.EXIFRating = 4;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 4); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 4;
+        }
     }
     }
 
 
     public static async Task Set5Star()
     public static async Task Set5Star()
@@ -753,8 +768,11 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.FullName, 5); });
-        Vm.EXIFRating = 5;
+        await Task.Run(() => { EXIFHelper.SetEXIFRating(Vm.PicViewer.FileInfo.CurrentValue.FullName, 5); });
+        if (Vm.Exif is not null)
+        {
+            Vm.Exif.ExifRating.Value = 5;
+        }
     }
     }
 
 
     #endregion
     #endregion
@@ -768,12 +786,12 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        if (string.IsNullOrEmpty(Vm.Exif.GoogleLink))
+        if (string.IsNullOrEmpty(Vm.Exif.GoogleLink.CurrentValue))
         {
         {
             return;
             return;
         }
         }
 
 
-        await Task.Run(() => ProcessHelper.OpenLink(Vm.Exif.GoogleLink));
+        await Task.Run(() => ProcessHelper.OpenLink(Vm.Exif.GoogleLink.CurrentValue));
     }
     }
     
     
     public static async Task OpenBingMaps()
     public static async Task OpenBingMaps()
@@ -783,12 +801,12 @@ public static class FunctionsMapper
         {
         {
             return;
             return;
         }
         }
-        if (string.IsNullOrEmpty(Vm.Exif.BingLink))
+        if (string.IsNullOrEmpty(Vm.Exif.BingLink.CurrentValue))
         {
         {
             return;
             return;
         }
         }
 
 
-        await Task.Run(() => ProcessHelper.OpenLink(Vm.Exif.BingLink));
+        await Task.Run(() => ProcessHelper.OpenLink(Vm.Exif.BingLink.CurrentValue));
     }
     }
 
 
     #endregion
     #endregion
@@ -799,25 +817,25 @@ public static class FunctionsMapper
         await SetAsWallpaperFilled();
         await SetAsWallpaperFilled();
 
 
     public static async Task SetAsWallpaperTiled() =>
     public static async Task SetAsWallpaperTiled() =>
-        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.FullName, 0)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.CurrentValue.FullName, 0)).ConfigureAwait(false);
     
     
     public static async Task SetAsWallpaperCentered() =>
     public static async Task SetAsWallpaperCentered() =>
-        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.FullName, 1)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.CurrentValue.FullName, 1)).ConfigureAwait(false);
     
     
     public static async Task SetAsWallpaperStretched() =>
     public static async Task SetAsWallpaperStretched() =>
-        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.FullName, 2)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.CurrentValue.FullName, 2)).ConfigureAwait(false);
     
     
     public static async Task SetAsWallpaperFitted() =>
     public static async Task SetAsWallpaperFitted() =>
-        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.FullName, 3)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.CurrentValue.FullName, 3)).ConfigureAwait(false);
     
     
     public static async Task SetAsWallpaperFilled() =>
     public static async Task SetAsWallpaperFilled() =>
-        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.FullName, 4)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsWallpaper(Vm.PicViewer.FileInfo.CurrentValue.FullName, 4)).ConfigureAwait(false);
     
     
     public static async Task SetAsLockscreenCentered() =>
     public static async Task SetAsLockscreenCentered() =>
-        await Task.Run(() => Vm.PlatformService.SetAsLockScreen(Vm.PicViewer.FileInfo.FullName)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsLockScreen(Vm.PicViewer.FileInfo.CurrentValue.FullName)).ConfigureAwait(false);
     
     
     public static async Task SetAsLockScreen() =>
     public static async Task SetAsLockScreen() =>
-        await Task.Run(() => Vm.PlatformService.SetAsLockScreen(Vm.PicViewer.FileInfo.FullName)).ConfigureAwait(false);
+        await Task.Run(() => Vm.PlatformService.SetAsLockScreen(Vm.PicViewer.FileInfo.CurrentValue.FullName)).ConfigureAwait(false);
 
 
     #endregion
     #endregion
 
 
@@ -834,9 +852,9 @@ public static class FunctionsMapper
         var getFromArgs = false;
         var getFromArgs = false;
         if (Vm?.PicViewer.FileInfo is not null)
         if (Vm?.PicViewer.FileInfo is not null)
         {
         {
-            if (Vm.PicViewer.FileInfo.Exists)
+            if (Vm.PicViewer.FileInfo.CurrentValue.Exists)
             {
             {
-                openFile = Vm.PicViewer.FileInfo.FullName;
+                openFile = Vm.PicViewer.FileInfo.CurrentValue.FullName;
             }
             }
             else
             else
             {
             {
@@ -877,10 +895,6 @@ public static class FunctionsMapper
     public static async Task ShowRecentHistoryFile() =>
     public static async Task ShowRecentHistoryFile() =>
         await Task.Run(() => Vm?.PlatformService?.OpenWith(FileHistoryManager.CurrentFileHistoryFile)).ConfigureAwait(false);
         await Task.Run(() => Vm?.PlatformService?.OpenWith(FileHistoryManager.CurrentFileHistoryFile)).ConfigureAwait(false);
     
     
-    /// <inheritdoc cref="SettingsUpdater.ToggleUsingTouchpad(MainViewModel)" />
-    public static async Task ToggleUsingTouchpad() =>
-        await SettingsUpdater.ToggleUsingTouchpad(Vm).ConfigureAwait(false);
-    
     public static async Task ToggleOpeningInSameWindow() =>
     public static async Task ToggleOpeningInSameWindow() =>
         await SettingsUpdater.ToggleOpeningInSameWindow(Vm).ConfigureAwait(false);
         await SettingsUpdater.ToggleOpeningInSameWindow(Vm).ConfigureAwait(false);
 
 

+ 59 - 33
src/PicView.Avalonia/Gallery/GalleryFunctions.cs

@@ -8,12 +8,41 @@ using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.Views.UC;
 using PicView.Avalonia.Views.UC;
 using PicView.Core.Gallery;
 using PicView.Core.Gallery;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
+using PicView.Core.Sizing;
 
 
 namespace PicView.Avalonia.Gallery;
 namespace PicView.Avalonia.Gallery;
 
 
 public static class GalleryFunctions
 public static class GalleryFunctions
 {
 {
-    public static bool RenameGalleryItem(int oldIndex, int newIndex, string newFileLocation, string newName, MainViewModel? vm)
+    public static double GetGalleryHeight(MainViewModel vm)
+    {
+        if (vm?.Gallery is not { } gallery)
+        {
+            return 0;
+        }
+
+        if (!Settings.Gallery.IsBottomGalleryShown || vm.PicViewer.IsSingleImage.CurrentValue || Slideshow.IsRunning)
+        {
+            return 0;
+        }
+
+        if (Settings.WindowProperties.Fullscreen)
+        {
+            return Settings.Gallery.IsBottomGalleryShown
+                ? gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue + (SizeDefaults.ScrollbarSize - 1)
+                : 0;
+        }
+
+        if (!Settings.Gallery.ShowBottomGalleryInHiddenUI && !vm.MainWindow.IsUIShown.CurrentValue)
+        {
+            return 0;
+        }
+
+        return gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue + (SizeDefaults.ScrollbarSize - 1);
+    }
+
+    public static bool RenameGalleryItem(int oldIndex, int newIndex, string newFileLocation, string newName,
+        MainViewModel? vm)
     {
     {
         var mainView = UIHelper.GetMainView;
         var mainView = UIHelper.GetMainView;
 
 
@@ -42,12 +71,8 @@ public static class GalleryFunctions
         {
         {
             return Rename();
             return Rename();
         }
         }
-        Dispatcher.UIThread.InvokeAsync(Rename);
 
 
-        if (vm != null)
-        {
-            vm.SelectedGalleryItemIndex = NavigationManager.GetCurrentIndex;
-        }
+        Dispatcher.UIThread.InvokeAsync(Rename);
 
 
         return true;
         return true;
 
 
@@ -57,6 +82,7 @@ public static class GalleryFunctions
             {
             {
                 return false;
                 return false;
             }
             }
+
             galleryItem.FileName.Text = newName;
             galleryItem.FileName.Text = newName;
             galleryItem.FileLocation.Text = newFileLocation;
             galleryItem.FileLocation.Text = newFileLocation;
             if (oldIndex == newIndex)
             if (oldIndex == newIndex)
@@ -64,6 +90,7 @@ public static class GalleryFunctions
                 galleryListBox.Items[oldIndex] = galleryItem;
                 galleryListBox.Items[oldIndex] = galleryItem;
                 return true;
                 return true;
             }
             }
+
             if (newIndex >= 0 && newIndex < galleryListBox.Items.Count)
             if (newIndex >= 0 && newIndex < galleryListBox.Items.Count)
             {
             {
                 galleryListBox.Items.RemoveAt(oldIndex);
                 galleryListBox.Items.RemoveAt(oldIndex);
@@ -74,7 +101,7 @@ public static class GalleryFunctions
             return false;
             return false;
         }
         }
     }
     }
-    
+
     public static bool RemoveGalleryItem(int index, MainViewModel? vm)
     public static bool RemoveGalleryItem(int index, MainViewModel? vm)
     {
     {
         var mainView = UIHelper.GetMainView;
         var mainView = UIHelper.GetMainView;
@@ -94,11 +121,6 @@ public static class GalleryFunctions
             Dispatcher.UIThread.InvokeAsync(Removal);
             Dispatcher.UIThread.InvokeAsync(Removal);
         }
         }
 
 
-        if (vm != null)
-        {
-            vm.SelectedGalleryItemIndex = NavigationManager.GetCurrentIndex;
-        }
-
         return true;
         return true;
 
 
         void Removal()
         void Removal()
@@ -108,10 +130,12 @@ public static class GalleryFunctions
             {
             {
                 return;
                 return;
             }
             }
+
             if (galleryListBox.Items[removalIndex] is not GalleryItem galleryItem)
             if (galleryListBox.Items[removalIndex] is not GalleryItem galleryItem)
             {
             {
                 return;
                 return;
             }
             }
+
             galleryListBox.Items.Remove(galleryItem);
             galleryListBox.Items.Remove(galleryItem);
             if (galleryItem.GalleryImage.Source is IDisposable galleryImage)
             if (galleryItem.GalleryImage.Source is IDisposable galleryImage)
             {
             {
@@ -120,7 +144,8 @@ public static class GalleryFunctions
         }
         }
     }
     }
 
 
-    public static async Task<bool> AddGalleryItem(int index, FileInfo fileInfo, MainViewModel? vm, DispatcherPriority? priority = null)
+    public static async Task<bool> AddGalleryItem(int index, FileInfo fileInfo, MainViewModel? vm,
+        DispatcherPriority? priority = null)
     {
     {
         var mainView = UIHelper.GetMainView;
         var mainView = UIHelper.GetMainView;
 
 
@@ -131,7 +156,7 @@ public static class GalleryFunctions
         }
         }
 
 
         GalleryItem? galleryItem;
         GalleryItem? galleryItem;
-        var thumb = await GetThumbnails.GetThumbAsync(fileInfo, (uint)vm.GetGalleryItemHeight);
+        var thumb = await GetThumbnails.GetThumbAsync(fileInfo, (uint)vm.Gallery.GalleryItem.ItemHeight.Value);
         var galleryThumbInfo = GalleryThumbInfo.GalleryThumbHolder.GetThumbData(fileInfo);
         var galleryThumbInfo = GalleryThumbInfo.GalleryThumbHolder.GetThumbData(fileInfo);
         try
         try
         {
         {
@@ -173,7 +198,7 @@ public static class GalleryFunctions
                 {
                 {
                     galleryListBox.Items.Add(galleryItem);
                     galleryListBox.Items.Add(galleryItem);
                 }
                 }
-                
+
                 var isSvg = fileInfo.Extension.Equals(".svg", StringComparison.OrdinalIgnoreCase) ||
                 var isSvg = fileInfo.Extension.Equals(".svg", StringComparison.OrdinalIgnoreCase) ||
                             fileInfo.Extension.Equals(".svgz", StringComparison.OrdinalIgnoreCase);
                             fileInfo.Extension.Equals(".svgz", StringComparison.OrdinalIgnoreCase);
                 if (isSvg)
                 if (isSvg)
@@ -262,7 +287,7 @@ public static class GalleryFunctions
         {
         {
             Dispatcher.UIThread.Post(Center);
             Dispatcher.UIThread.Post(Center);
         }
         }
-        
+
         return;
         return;
 
 
         void Center()
         void Center()
@@ -270,11 +295,12 @@ public static class GalleryFunctions
             var mainView = UIHelper.GetMainView;
             var mainView = UIHelper.GetMainView;
 
 
             var galleryListBox = mainView.GalleryView.GalleryListBox;
             var galleryListBox = mainView.GalleryView.GalleryListBox;
-            if (vm.SelectedGalleryItemIndex < 0 || vm.SelectedGalleryItemIndex >= galleryListBox.Items.Count)
+            if (vm.PicViewer.Index.Value < 0 || vm.PicViewer.Index.Value >= galleryListBox.Items.Count)
             {
             {
                 return;
                 return;
             }
             }
-            if (galleryListBox.Items[vm.SelectedGalleryItemIndex] is GalleryItem centerItem)
+
+            if (galleryListBox.Items[vm.PicViewer.Index.CurrentValue] is GalleryItem centerItem)
             {
             {
                 galleryListBox.ScrollToCenterOfItem(centerItem);
                 galleryListBox.ScrollToCenterOfItem(centerItem);
             }
             }
@@ -299,15 +325,15 @@ public static class GalleryFunctions
             {
             {
                 // Switch to bottom gallery
                 // Switch to bottom gallery
                 IsFullGalleryOpen = false;
                 IsFullGalleryOpen = false;
-                vm.GalleryMode = GalleryMode.FullToBottom;
-                vm.GetGalleryItemHeight = vm.GetBottomGalleryItemHeight;
+                vm.Gallery.GalleryMode.Value = GalleryMode.FullToBottom;
+                vm.Gallery.GalleryItem.ItemHeight.Value = vm.Gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue;
             }
             }
             else
             else
             {
             {
                 // Switch to full gallery
                 // Switch to full gallery
                 IsFullGalleryOpen = true;
                 IsFullGalleryOpen = true;
-                vm.GalleryMode = GalleryMode.BottomToFull;
-                vm.GetGalleryItemHeight = vm.GetFullGalleryItemHeight;
+                vm.Gallery.GalleryMode.Value = GalleryMode.BottomToFull;
+                vm.Gallery.GalleryItem.ItemHeight.Value = vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.CurrentValue;;
             }
             }
         }
         }
         else
         else
@@ -316,18 +342,18 @@ public static class GalleryFunctions
             {
             {
                 // close full gallery
                 // close full gallery
                 IsFullGalleryOpen = false;
                 IsFullGalleryOpen = false;
-                vm.GalleryMode = GalleryMode.FullToClosed;
+                vm.Gallery.GalleryMode.Value = GalleryMode.FullToClosed;
             }
             }
             else
             else
             {
             {
                 // open full gallery
                 // open full gallery
                 IsFullGalleryOpen = true;
                 IsFullGalleryOpen = true;
-                vm.GalleryMode = GalleryMode.ClosedToFull;
-                vm.GetGalleryItemHeight = vm.GetFullGalleryItemHeight;
+                vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToFull;
+                vm.Gallery.GalleryItem.ItemHeight.Value = vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.CurrentValue;
             }
             }
         }
         }
 
 
-        
+
         _ = Task.Run(() => GalleryLoad.LoadGallery(vm, NavigationManager.GetInitialFileInfo?.DirectoryName));
         _ = Task.Run(() => GalleryLoad.LoadGallery(vm, NavigationManager.GetInitialFileInfo?.DirectoryName));
     }
     }
 
 
@@ -342,8 +368,8 @@ public static class GalleryFunctions
 
 
         if (Settings.Gallery.IsBottomGalleryShown)
         if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.BottomToClosed;
-            vm.Translation.IsShowingBottomGallery = TranslationManager.Translation.ShowBottomGallery;
+            vm.Gallery.GalleryMode.Value = GalleryMode.BottomToClosed;
+            vm.Translation.IsShowingBottomGallery.Value = TranslationManager.Translation.ShowBottomGallery;
             Settings.Gallery.IsBottomGalleryShown = false;
             Settings.Gallery.IsBottomGalleryShown = false;
             IsFullGalleryOpen = false;
             IsFullGalleryOpen = false;
             return;
             return;
@@ -353,11 +379,11 @@ public static class GalleryFunctions
         Settings.Gallery.IsBottomGalleryShown = true;
         Settings.Gallery.IsBottomGalleryShown = true;
         if (NavigationManager.CanNavigate(vm))
         if (NavigationManager.CanNavigate(vm))
         {
         {
-            vm.GalleryMode = GalleryMode.ClosedToBottom;
+            vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
         }
         }
 
 
-        vm.Translation.IsShowingBottomGallery = TranslationManager.Translation.HideBottomGallery;
-        vm.IsBottomGalleryShown = true;
+        vm.Translation.IsShowingBottomGallery.Value = TranslationManager.Translation.HideBottomGallery;
+        vm.Gallery.IsBottomGalleryShown.Value = true;
         if (!NavigationManager.CanNavigate(vm))
         if (!NavigationManager.CanNavigate(vm))
         {
         {
             return;
             return;
@@ -368,8 +394,8 @@ public static class GalleryFunctions
 
 
     public static void OpenBottomGallery(MainViewModel vm)
     public static void OpenBottomGallery(MainViewModel vm)
     {
     {
-        vm.GalleryMode = GalleryMode.ClosedToBottom;
-        vm.GalleryVerticalAlignment = VerticalAlignment.Bottom;
+        vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
+        vm.Gallery.GalleryVerticalAlignment.Value = VerticalAlignment.Bottom;
     }
     }
 
 
     public static void CloseGallery(MainViewModel vm)
     public static void CloseGallery(MainViewModel vm)

+ 2 - 0
src/PicView.Avalonia/Gallery/GalleryHelper.cs

@@ -1,10 +1,12 @@
 using Avalonia.Media;
 using Avalonia.Media;
+using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 
 
 namespace PicView.Avalonia.Gallery;
 namespace PicView.Avalonia.Gallery;
 
 
 public static class GalleryHelper
 public static class GalleryHelper
 {
 {
+    public static void SetGalleryItemStretch(string value) => SetGalleryItemStretch(value, UIHelper.GetMainView.DataContext as MainViewModel);
     public static void SetGalleryItemStretch(string value, MainViewModel vm)
     public static void SetGalleryItemStretch(string value, MainViewModel vm)
     {
     {
         if (value.Equals("Square", StringComparison.OrdinalIgnoreCase))
         if (value.Equals("Square", StringComparison.OrdinalIgnoreCase))

+ 5 - 6
src/PicView.Avalonia/Gallery/GalleryLoad.cs

@@ -61,14 +61,14 @@ public static class GalleryLoad
         // Make sure height is set
         // Make sure height is set
         if (Settings.Gallery.IsBottomGalleryShown && !GalleryFunctions.IsFullGalleryOpen)
         if (Settings.Gallery.IsBottomGalleryShown && !GalleryFunctions.IsFullGalleryOpen)
         {
         {
-            vm.GetGalleryItemHeight = vm.GetBottomGalleryItemHeight;
+            vm.Gallery.GalleryItem.ItemHeight.Value = vm.Gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue;
         }
         }
 
 
         _cancellationTokenSource = new CancellationTokenSource();
         _cancellationTokenSource = new CancellationTokenSource();
         _currentDirectory = currentDirectory;
         _currentDirectory = currentDirectory;
         IsLoading = true;
         IsLoading = true;
         var index = NavigationManager.GetCurrentIndex;
         var index = NavigationManager.GetCurrentIndex;
-        var galleryItemSize = Math.Max(vm.GetBottomGalleryItemHeight, vm.GetFullGalleryItemHeight);
+        var galleryItemSize = Math.Max(vm.Gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue, vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.CurrentValue);
 
 
         var endIndex = NavigationManager.GetCount;
         var endIndex = NavigationManager.GetCount;
         // Set priority low when loading excess images to ensure app responsiveness
         // Set priority low when loading excess images to ensure app responsiveness
@@ -119,8 +119,7 @@ public static class GalleryLoad
                     {
                     {
                         return;
                         return;
                     }
                     }
-
-                    vm.SelectedGalleryItemIndex = i;
+                    
                     galleryListBox.SelectedItem = galleryItem;
                     galleryListBox.SelectedItem = galleryItem;
                 }, priority, _cancellationTokenSource.Token);
                 }, priority, _cancellationTokenSource.Token);
             }
             }
@@ -295,10 +294,10 @@ public static class GalleryLoad
             // Check if the bottom gallery should be shown
             // Check if the bottom gallery should be shown
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                if (vm.GalleryMode is GalleryMode.BottomToClosed or GalleryMode.FullToClosed or GalleryMode.Closed)
+                if (vm.Gallery.GalleryMode.CurrentValue is GalleryMode.BottomToClosed or GalleryMode.FullToClosed or GalleryMode.Closed)
                 {
                 {
                     // Trigger animation to show it
                     // Trigger animation to show it
-                    vm.GalleryMode = GalleryMode.ClosedToBottom;
+                    vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
                 }
                 }
             }
             }
 
 

+ 7 - 7
src/PicView.Avalonia/Gallery/GalleryNavigation.cs

@@ -63,14 +63,14 @@ public static class GalleryNavigation
         {
         {
             var listbox = UIHelper.GetGalleryView.GalleryListBox;
             var listbox = UIHelper.GetGalleryView.GalleryListBox;
 
 
-            if (listbox is null || vm.SelectedGalleryItemIndex < 0 || vm.SelectedGalleryItemIndex >= listbox.Items.Count)
+            if (listbox is null || vm.PicViewer.Index.CurrentValue < 0 || vm.PicViewer.Index.CurrentValue >= listbox.Items.Count)
             {
             {
                 return;
                 return;
             }
             }
 
 
             try
             try
             {
             {
-                listbox.ScrollToCenterOfItem(listbox.Items[vm.SelectedGalleryItemIndex] as GalleryItem);
+                listbox.ScrollToCenterOfItem(listbox.Items[vm.PicViewer.Index.CurrentValue] as GalleryItem);
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {
@@ -83,7 +83,7 @@ public static class GalleryNavigation
     
     
     public static void NavigateGallery(Direction direction, MainViewModel vm)
     public static void NavigateGallery(Direction direction, MainViewModel vm)
     {
     {
-        var highlightedGalleryItem = vm.SelectedGalleryItemIndex;
+        var highlightedGalleryItem = vm.PicViewer.Index.CurrentValue;
         var galleryItems = GetGalleryItems();
         var galleryItems = GetGalleryItems();
 
 
         if (highlightedGalleryItem < 0 || highlightedGalleryItem >= galleryItems.Count)
         if (highlightedGalleryItem < 0 || highlightedGalleryItem >= galleryItems.Count)
@@ -110,7 +110,7 @@ public static class GalleryNavigation
     
     
     public static void NavigateGallery(bool last, MainViewModel vm)
     public static void NavigateGallery(bool last, MainViewModel vm)
     {
     {
-        var highlightedGalleryItem = vm.SelectedGalleryItemIndex;
+        var highlightedGalleryItem = vm.PicViewer.Index.CurrentValue;
         var galleryItems = GetGalleryItems();
         var galleryItems = GetGalleryItems();
         
         
         if (highlightedGalleryItem < 0 || highlightedGalleryItem >= galleryItems.Count)
         if (highlightedGalleryItem < 0 || highlightedGalleryItem >= galleryItems.Count)
@@ -159,7 +159,7 @@ public static class GalleryNavigation
 
 
     public static void SetHighlightedGalleryItem(MainViewModel vm, int index)
     public static void SetHighlightedGalleryItem(MainViewModel vm, int index)
     {
     {
-        vm.SelectedGalleryItemIndex = index;
+        vm.PicViewer.Index.Value = index;
         CenterScrollToSelectedItem(vm); // Ensure the selected item is in view
         CenterScrollToSelectedItem(vm); // Ensure the selected item is in view
     }
     }
 
 
@@ -176,9 +176,9 @@ public static class GalleryNavigation
             return;
             return;
         }
         }
         GalleryFunctions.ToggleGallery(vm);
         GalleryFunctions.ToggleGallery(vm);
-        if (vm.SelectedGalleryItemIndex != NavigationManager.GetCurrentIndex) 
+        if (vm.PicViewer.Index.CurrentValue != NavigationManager.GetCurrentIndex) 
         {
         {
-            await NavigationManager.Navigate(vm.SelectedGalleryItemIndex, vm).ConfigureAwait(false);
+            await NavigationManager.Navigate(vm.PicViewer.Index.CurrentValue, vm).ConfigureAwait(false);
         }
         }
     }
     }
     
     

+ 56 - 56
src/PicView.Avalonia/Gallery/GalleryStretch.cs

@@ -7,45 +7,45 @@ public static class GalleryStretchMode
     public static void DetermineStretchMode(MainViewModel vm)
     public static void DetermineStretchMode(MainViewModel vm)
     {
     {
         // Reset all boolean properties
         // Reset all boolean properties
-        vm.IsUniformMenuChecked = false;
-        vm.IsUniformBottomChecked = false;
-        vm.IsUniformFullChecked = false;
+        vm.Gallery.IsUniformMenuChecked.Value = false;
+        vm.Gallery.IsUniformBottomChecked.Value = false;
+        vm.Gallery.IsUniformFullChecked.Value = false;
         
         
-        vm.IsUniformToFillMenuChecked = false;
-        vm.IsUniformToFillBottomChecked = false;
-        vm.IsUniformToFillFullChecked = false;
+        vm.Gallery.IsUniformToFillMenuChecked.Value = false;
+        vm.Gallery.IsUniformToFillBottomChecked.Value = false;
+        vm.Gallery.IsUniformToFillFullChecked.Value = false;
         
         
-        vm.IsFillMenuChecked = false;
-        vm.IsFillBottomChecked = false;
-        vm.IsFillFullChecked = false;
+        vm.Gallery.IsFillMenuChecked.Value = false;
+        vm.Gallery.IsFillBottomChecked.Value = false;
+        vm.Gallery.IsFillFullChecked.Value = false;
         
         
-        vm.IsNoneMenuChecked = false;
-        vm.IsNoneBottomChecked = false;
-        vm.IsNoneFullChecked = false;
+        vm.Gallery.IsNoneMenuChecked.Value = false;
+        vm.Gallery.IsNoneBottomChecked.Value = false;
+        vm.Gallery.IsNoneFullChecked.Value = false;
         
         
-        vm.IsSquareMenuChecked = false;
-        vm.IsSquareBottomChecked = false;
-        vm.IsSquareFullChecked = false;
+        vm.Gallery.IsSquareMenuChecked.Value = false;
+        vm.Gallery.IsSquareBottomChecked.Value = false;
+        vm.Gallery.IsSquareFullChecked.Value = false;
         
         
-        vm.IsFillSquareMenuChecked = false;
-        vm.IsFillSquareBottomChecked = false;
-        vm.IsFillSquareFullChecked = false;
+        vm.Gallery.IsFillSquareMenuChecked.Value = false;
+        vm.Gallery.IsFillSquareBottomChecked.Value = false;
+        vm.Gallery.IsFillSquareFullChecked.Value = false;
 
 
         if (Settings.Gallery.FullGalleryStretchMode.Equals("Square", StringComparison.OrdinalIgnoreCase))
         if (Settings.Gallery.FullGalleryStretchMode.Equals("Square", StringComparison.OrdinalIgnoreCase))
         {
         {
-            vm.IsSquareFullChecked = true;
+            vm.Gallery.IsSquareFullChecked.Value = true;
             if (GalleryFunctions.IsFullGalleryOpen)
             if (GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsSquareMenuChecked = true;
+                vm.Gallery.IsSquareMenuChecked.Value = true;
                 SetSquareStretch(vm);
                 SetSquareStretch(vm);
             }
             }
         }
         }
         else if (Settings.Gallery.FullGalleryStretchMode.Equals("FillSquare", StringComparison.OrdinalIgnoreCase))
         else if (Settings.Gallery.FullGalleryStretchMode.Equals("FillSquare", StringComparison.OrdinalIgnoreCase))
         {
         {
-            vm.IsFillSquareFullChecked = true;
+            vm.Gallery.IsFillSquareFullChecked.Value = true;
             if (GalleryFunctions.IsFullGalleryOpen)
             if (GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsFillSquareMenuChecked = true;
+                vm.Gallery.IsFillSquareMenuChecked.Value = true;
                 SetSquareFillStretch(vm);
                 SetSquareFillStretch(vm);
             }
             }
         }
         }
@@ -59,31 +59,31 @@ public static class GalleryStretchMode
         }
         }
         else
         else
         {
         {
-            vm.GetGalleryItemWidth = double.NaN;
+            vm.Gallery.GalleryItem.ItemWidth.Value = double.NaN;
             if (GalleryFunctions.IsFullGalleryOpen)
             if (GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsUniformMenuChecked = true;
+                vm.Gallery.IsUniformMenuChecked.Value = true;
                 SetGalleryStretch(vm, Stretch.Uniform);
                 SetGalleryStretch(vm, Stretch.Uniform);
             }
             }
-            vm.IsUniformFullChecked = true;
+            vm.Gallery.IsUniformFullChecked.Value = true;
         }
         }
         
         
 
 
         if (Settings.Gallery.BottomGalleryStretchMode.Equals("Square", StringComparison.OrdinalIgnoreCase))
         if (Settings.Gallery.BottomGalleryStretchMode.Equals("Square", StringComparison.OrdinalIgnoreCase))
         {
         {
-            vm.IsSquareBottomChecked = true;
+            vm.Gallery.IsSquareBottomChecked.Value = true;
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsSquareMenuChecked = true;
+                vm.Gallery.IsSquareMenuChecked.Value = true;
                 SetSquareStretch(vm);
                 SetSquareStretch(vm);
             }
             }
         }
         }
         else if (Settings.Gallery.BottomGalleryStretchMode.Equals("FillSquare", StringComparison.OrdinalIgnoreCase))
         else if (Settings.Gallery.BottomGalleryStretchMode.Equals("FillSquare", StringComparison.OrdinalIgnoreCase))
         {
         {
-            vm.IsFillSquareBottomChecked = true;
+            vm.Gallery.IsFillSquareBottomChecked.Value = true;
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsFillSquareMenuChecked = true;
+                vm.Gallery.IsFillSquareMenuChecked.Value = true;
                 SetSquareFillStretch(vm);
                 SetSquareFillStretch(vm);
             }
             }
         }
         }
@@ -97,10 +97,10 @@ public static class GalleryStretchMode
         }
         }
         else
         else
         {
         {
-            vm.IsUniformBottomChecked = true;
+            vm.Gallery.IsUniformBottomChecked.Value = true;
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
-                vm.IsUniformMenuChecked = true;
+                vm.Gallery.IsUniformMenuChecked.Value = true;
                 SetGalleryStretch(vm, Stretch.Uniform);
                 SetGalleryStretch(vm, Stretch.Uniform);
             }
             }
         }
         }
@@ -115,82 +115,82 @@ public static class GalleryStretchMode
                 case Stretch.Uniform:
                 case Stretch.Uniform:
                     if (GalleryFunctions.IsFullGalleryOpen)
                     if (GalleryFunctions.IsFullGalleryOpen)
                     {
                     {
-                        vm.IsUniformFullChecked = true;
+                        vm.Gallery.IsUniformFullChecked.Value = true;
                         if (isFullGallery)
                         if (isFullGallery)
                         {
                         {
-                            vm.IsUniformMenuChecked = true;
+                            vm.Gallery.IsUniformMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     else
                     else
                     {
                     {
-                        vm.IsUniformBottomChecked = true;
+                        vm.Gallery.IsUniformBottomChecked.Value = true;
                         if (!isFullGallery)
                         if (!isFullGallery)
                         {
                         {
-                            vm.IsUniformMenuChecked = true;
+                            vm.Gallery.IsUniformMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     break;
                     break;
                 case Stretch.UniformToFill:
                 case Stretch.UniformToFill:
                     if (GalleryFunctions.IsFullGalleryOpen)
                     if (GalleryFunctions.IsFullGalleryOpen)
                     {
                     {
-                        vm.IsUniformToFillFullChecked = true;
+                        vm.Gallery.IsUniformToFillFullChecked.Value = true;
                         if (isFullGallery)
                         if (isFullGallery)
                         {
                         {
-                            vm.IsUniformToFillMenuChecked = true;
+                            vm.Gallery.IsUniformToFillMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     else
                     else
                     {
                     {
-                        vm.IsUniformToFillBottomChecked = true;
+                        vm.Gallery.IsUniformToFillBottomChecked.Value = true;
                         if (!isFullGallery)
                         if (!isFullGallery)
                         {
                         {
-                            vm.IsUniformToFillMenuChecked = true;
+                            vm.Gallery.IsUniformToFillMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     break;
                     break;
                 case Stretch.Fill:
                 case Stretch.Fill:
                     if (GalleryFunctions.IsFullGalleryOpen)
                     if (GalleryFunctions.IsFullGalleryOpen)
                     {
                     {
-                        vm.IsFillFullChecked = true;
+                        vm.Gallery.IsFillFullChecked.Value = true;
                         if (isFullGallery)
                         if (isFullGallery)
                         {
                         {
-                            vm.IsFillMenuChecked = true;
+                            vm.Gallery.IsFillMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     else
                     else
                     {
                     {
-                        vm.IsFillBottomChecked = true;
+                        vm.Gallery.IsFillBottomChecked.Value = true;
                         if (!isFullGallery)
                         if (!isFullGallery)
                         {
                         {
-                            vm.IsFillMenuChecked = true;
+                            vm.Gallery.IsFillMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     break;
                     break;
                 case Stretch.None:
                 case Stretch.None:
                     if (GalleryFunctions.IsFullGalleryOpen)
                     if (GalleryFunctions.IsFullGalleryOpen)
                     {
                     {
-                        vm.IsNoneFullChecked = true;
+                        vm.Gallery.IsNoneFullChecked.Value = true;
                         if (isFullGallery)
                         if (isFullGallery)
                         {
                         {
-                            vm.IsNoneMenuChecked = true;
+                            vm.Gallery.IsNoneMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     else
                     else
                     {
                     {
-                        vm.IsNoneBottomChecked = true;
+                        vm.Gallery.IsNoneBottomChecked.Value = true;
                         if (!isFullGallery)
                         if (!isFullGallery)
                         {
                         {
-                            vm.IsNoneMenuChecked = true;
+                            vm.Gallery.IsNoneMenuChecked.Value = true;
                         }
                         }
                     }
                     }
                     break;
                     break;
                 default:
                 default:
                     if (!GalleryFunctions.IsFullGalleryOpen)
                     if (!GalleryFunctions.IsFullGalleryOpen)
                     {
                     {
-                        vm.IsUniformMenuChecked = true;
+                        vm.Gallery.IsUniformMenuChecked.Value = true;
                     }
                     }
-                    vm.IsUniformFullChecked = true;
-                    vm.IsUniformBottomChecked = true;
+                    vm.Gallery.IsUniformFullChecked.Value = true;
+                    vm.Gallery.IsUniformBottomChecked.Value = true;
                     break;
                     break;
             }
             }
         }
         }
@@ -198,20 +198,20 @@ public static class GalleryStretchMode
     
     
     public static void SetGalleryStretch(MainViewModel vm, Stretch stretch)
     public static void SetGalleryStretch(MainViewModel vm, Stretch stretch)
     {
     {
-        vm.GetGalleryItemWidth = double.NaN;
-        vm.GalleryStretch = stretch;
+        vm.Gallery.GalleryItem.ItemWidth.Value = double.NaN;
+        vm.Gallery.GalleryStretch.Value = stretch;
     }
     }
 
 
     public static void SetSquareStretch(MainViewModel vm)
     public static void SetSquareStretch(MainViewModel vm)
     {
     {
-        vm.GetGalleryItemWidth = vm.GetGalleryItemHeight;
-        vm.GalleryStretch = Stretch.Uniform;
+        vm.Gallery.GalleryItem.ItemWidth.Value  = vm.Gallery.GalleryItem.ItemHeight.Value;
+        vm.Gallery.GalleryStretch.Value = Stretch.Uniform;
     }
     }
     
     
     public static void SetSquareFillStretch(MainViewModel vm)
     public static void SetSquareFillStretch(MainViewModel vm)
     {
     {
-        vm.GetGalleryItemWidth = vm.GetGalleryItemHeight;
-        vm.GalleryStretch = Stretch.Fill;
+        vm.Gallery.GalleryItem.ItemWidth.Value  = vm.Gallery.GalleryItem.ItemHeight.Value;;
+        vm.Gallery.GalleryStretch.Value = Stretch.Fill;
     }
     }
 
 
     public static void ChangeBottomGalleryItemStretch(MainViewModel vm, Stretch stretch)
     public static void ChangeBottomGalleryItemStretch(MainViewModel vm, Stretch stretch)

+ 5 - 5
src/PicView.Avalonia/ImageHandling/ImageFormatConverter.cs

@@ -28,7 +28,7 @@ public static class ImageFormatConverter
         // Primary case: Handle effect applied or empty path by saving current ImageSource
         // Primary case: Handle effect applied or empty path by saving current ImageSource
         if (vm.PicViewer.EffectConfig is not null || string.IsNullOrWhiteSpace(path))
         if (vm.PicViewer.EffectConfig is not null || string.IsNullOrWhiteSpace(path))
         {
         {
-            if (vm.PicViewer.ImageSource is Bitmap bmp)
+            if (vm.PicViewer.ImageSource.CurrentValue is Bitmap bmp)
             {
             {
                 source = bmp;
                 source = bmp;
             }
             }
@@ -36,15 +36,15 @@ public static class ImageFormatConverter
         else if (NavigationManager.CanNavigate(vm) && !string.IsNullOrEmpty(path))
         else if (NavigationManager.CanNavigate(vm) && !string.IsNullOrEmpty(path))
         {
         {
             // Handle effects for the current file
             // Handle effects for the current file
-            if (vm.PicViewer.EffectConfig is not null && vm.PicViewer.FileInfo?.FullName == path)
+            if (vm.PicViewer.EffectConfig is not null && vm.PicViewer.FileInfo?.CurrentValue.FullName == path)
             {
             {
-                if (vm.PicViewer.ImageSource is Bitmap bmp)
+                if (vm.PicViewer.ImageSource.CurrentValue is Bitmap bmp)
                 {
                 {
                     source = bmp;
                     source = bmp;
                 }
                 }
             }
             }
             // Current path that's already in common format
             // Current path that's already in common format
-            else if (path == vm.PicViewer.FileInfo?.FullName)
+            else if (path == vm.PicViewer.FileInfo?.CurrentValue.FullName)
             {
             {
                 if (path.IsCommon())
                 if (path.IsCommon())
                 {
                 {
@@ -52,7 +52,7 @@ public static class ImageFormatConverter
                     return path;
                     return path;
                 }
                 }
 
 
-                if (vm.PicViewer.ImageSource is Bitmap bmp)
+                if (vm.PicViewer.ImageSource.CurrentValue is Bitmap bmp)
                 {
                 {
                     source = bmp;
                     source = bmp;
                 }
                 }

+ 1 - 1
src/PicView.Avalonia/ImageHandling/ImageOptimizer.cs

@@ -29,7 +29,7 @@ public static class ImageOptimizer
                 {
                 {
                     OptimalCompression = true
                     OptimalCompression = true
                 };
                 };
-                optimizer.LosslessCompress(vm.PicViewer.FileInfo.FullName);
+                optimizer.LosslessCompress(vm.PicViewer.FileInfo.CurrentValue.FullName);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {

+ 8 - 8
src/PicView.Avalonia/ImageTransformations/Rotation/RotationNavigation.cs

@@ -47,7 +47,7 @@ public static class RotationNavigation
         {
         {
             vm.ImageViewer.Rotate(angle);
             vm.ImageViewer.Rotate(angle);
         });
         });
-        vm.RotationAngle = angle;
+        vm.GlobalSettings.RotationAngle.Value = angle;
         await WindowResizing.SetSizeAsync(vm);
         await WindowResizing.SetSizeAsync(vm);
     }
     }
 
 
@@ -124,15 +124,15 @@ public static class RotationNavigation
     {
     {
         Dispatcher.UIThread.Invoke(() => { vm.ImageViewer.Flip(true); });
         Dispatcher.UIThread.Invoke(() => { vm.ImageViewer.Flip(true); });
         
         
-        if (vm.PicViewer.ScaleX == 1)
+        if (vm.PicViewer.ScaleX.CurrentValue == 1)
         {
         {
-            vm.PicViewer.ScaleX = -1;
-            vm.Translation.IsFlipped = vm.Translation.UnFlip;
+            vm.PicViewer.ScaleX.Value = -1;
+            vm.Translation.IsFlipped.Value = vm.Translation.UnFlip.CurrentValue;
         }
         }
         else
         else
         {
         {
-            vm.PicViewer.ScaleX = 1;
-            vm.Translation.IsFlipped = vm.Translation.Flip;
+            vm.PicViewer.ScaleX.Value = 1;
+            vm.Translation.IsFlipped.Value = vm.Translation.Flip.CurrentValue;
         }
         }
     }
     }
 
 
@@ -154,7 +154,7 @@ public static class RotationNavigation
 
 
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
-            if (vm.IsScrollingEnabled)
+            if (vm.GlobalSettings.IsScrollingEnabled.CurrentValue)
             {
             {
                 vm.ImageViewer.ImageScrollViewer.LineUp();
                 vm.ImageViewer.ImageScrollViewer.LineUp();
             }
             }
@@ -183,7 +183,7 @@ public static class RotationNavigation
 
 
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
-            if (vm.IsScrollingEnabled)
+            if (vm.GlobalSettings.IsScrollingEnabled.CurrentValue)
             {
             {
                 vm.ImageViewer.ImageScrollViewer.LineDown();
                 vm.ImageViewer.ImageScrollViewer.LineDown();
             }
             }

+ 9 - 9
src/PicView.Avalonia/ImageTransformations/Rotation/RotationTransformer.cs

@@ -24,10 +24,10 @@ public class RotationTransformer(
             return;
             return;
         }
         }
 
 
-        if (RotationHelper.IsValidRotation(vm.RotationAngle))
+        if (RotationHelper.IsValidRotation(vm.GlobalSettings.RotationAngle.CurrentValue))
         {
         {
-            var nextAngle = RotationHelper.Rotate(vm.RotationAngle, clockWise);
-            vm.RotationAngle = nextAngle switch
+            var nextAngle = RotationHelper.Rotate(vm.GlobalSettings.RotationAngle.CurrentValue, clockWise);
+            vm.GlobalSettings.RotationAngle.Value = nextAngle switch
             {
             {
                 360 => 0,
                 360 => 0,
                 -90 => 270,
                 -90 => 270,
@@ -36,10 +36,10 @@ public class RotationTransformer(
         }
         }
         else
         else
         {
         {
-            vm.RotationAngle = RotationHelper.NextRotationAngle(vm.RotationAngle, true);
+            vm.GlobalSettings.RotationAngle.Value = RotationHelper.NextRotationAngle(vm.GlobalSettings.RotationAngle.CurrentValue, true);
         }
         }
 
 
-        SetImageLayoutTransform(new RotateTransform(vm.RotationAngle));
+        SetImageLayoutTransform(new RotateTransform(vm.GlobalSettings.RotationAngle.CurrentValue));
         WindowResizing.SetSize(vm);
         WindowResizing.SetSize(vm);
         mainImage.InvalidateVisual();
         mainImage.InvalidateVisual();
     }
     }
@@ -74,7 +74,7 @@ public class RotationTransformer(
         
         
         _scaleTransform ??= new ScaleTransform();
         _scaleTransform ??= new ScaleTransform();
 
 
-        var prevScaleX = vm.PicViewer.ScaleX;
+        var prevScaleX = vm.PicViewer.ScaleX.CurrentValue;
         var newScaleX = prevScaleX == -1 ? 1 : -1;
         var newScaleX = prevScaleX == -1 ? 1 : -1;
 
 
         if (animate)
         if (animate)
@@ -103,9 +103,9 @@ public class RotationTransformer(
             return;
             return;
         }
         }
 
 
-        vm.PicViewer.ScaleX = scaleX;
-        vm.RotationAngle = rotationAngle;
-        imageLayoutTransformControl.RenderTransform = new ScaleTransform(vm.PicViewer.ScaleX, 1);
+        vm.PicViewer.ScaleX.Value = scaleX;
+        vm.GlobalSettings.RotationAngle.Value = rotationAngle;
+        imageLayoutTransformControl.RenderTransform = new ScaleTransform(vm.PicViewer.ScaleX.CurrentValue, 1);
         imageLayoutTransformControl.LayoutTransform = new RotateTransform(rotationAngle);
         imageLayoutTransformControl.LayoutTransform = new RotateTransform(rotationAngle);
 
 
         resetZoom?.Invoke();
         resetZoom?.Invoke();

+ 3 - 3
src/PicView.Avalonia/ImageTransformations/Zoom.cs

@@ -210,7 +210,7 @@ public class Zoom
             return;
             return;
         }
         }
 
 
-        vm.ZoomValue = zoomValue;
+        vm.GlobalSettings.ZoomValue.Value = zoomValue;
         if (!IsZoomed)
         if (!IsZoomed)
         {
         {
             return;
             return;
@@ -270,7 +270,7 @@ public class Zoom
             return;
             return;
         }
         }
 
 
-        vm.ZoomValue = 1;
+        vm.GlobalSettings.ZoomValue.Value = 1;
         TooltipHelper.StopTooltipMessage();
         TooltipHelper.StopTooltipMessage();
         TitleManager.SetTitle(vm);
         TitleManager.SetTitle(vm);
     }
     }
@@ -317,7 +317,7 @@ public class Zoom
 
 
         // Get the current rotation angle from the ViewModel
         // Get the current rotation angle from the ViewModel
         var vm = imageViewer.DataContext as MainViewModel;
         var vm = imageViewer.DataContext as MainViewModel;
-        var rotationAngle = vm?.RotationAngle ?? 0;
+        var rotationAngle = vm?.GlobalSettings.RotationAngle.CurrentValue ?? 0;
 
 
         // Apply rotation transformation to the mouse movement
         // Apply rotation transformation to the mouse movement
         var rotationRadians = rotationAngle * Math.PI / 180.0;
         var rotationRadians = rotationAngle * Math.PI / 180.0;

+ 2 - 2
src/PicView.Avalonia/Input/MainKeyboardShortcuts.cs

@@ -172,7 +172,7 @@ public static class MainKeyboardShortcuts
         // Handle cropping mode
         // Handle cropping mode
         if (CropFunctions.IsCropping)
         if (CropFunctions.IsCropping)
         {
         {
-            if (UIHelper.GetMainView.MainGrid.DataContext is MainViewModel { CurrentView: CropControl cropControl })
+            if (UIHelper.GetMainView.MainGrid.DataContext is MainViewModel { MainWindow.CurrentView.CurrentValue: CropControl cropControl })
             {
             {
                 await cropControl.KeyDownHandler(null, e);
                 await cropControl.KeyDownHandler(null, e);
             }
             }
@@ -192,7 +192,7 @@ public static class MainKeyboardShortcuts
         // Handle escape key
         // Handle escape key
         if (e.Key == Key.Escape)
         if (e.Key == Key.Escape)
         {
         {
-            if (UIHelper.GetMainView.DataContext as MainViewModel is { IsEditableTitlebarOpen: true })
+            if (UIHelper.GetMainView.DataContext as MainViewModel is { MainWindow.IsEditableTitlebarOpen.CurrentValue: true })
             {
             {
                 return true;
                 return true;
             }
             }

+ 2 - 2
src/PicView.Avalonia/LockScreen/LockScreenHelper.cs

@@ -19,7 +19,7 @@ public static class LockScreenHelper
             return;
             return;
         }
         }
         
         
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
 
 
         try
         try
         {
         {
@@ -49,7 +49,7 @@ public static class LockScreenHelper
         }
         }
         finally
         finally
         {
         {
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         }
         }
     }
     }
 }
 }

+ 16 - 14
src/PicView.Avalonia/Navigation/ErrorHandling.cs

@@ -34,42 +34,44 @@ public static class ErrorHandling
         return;
         return;
         void Start()
         void Start()
         {
         {
-            if (vm.CurrentView is not StartUpMenu)
+            if (vm.MainWindow.CurrentView.CurrentValue is not StartUpMenu)
             {
             {
                 var startUpMenu = new StartUpMenu();
                 var startUpMenu = new StartUpMenu();
                 if (Settings.WindowProperties.AutoFit)
                 if (Settings.WindowProperties.AutoFit)
                 {
                 {
                     startUpMenu.Width = SizeDefaults.WindowMinSize;
                     startUpMenu.Width = SizeDefaults.WindowMinSize;
                     startUpMenu.Height = SizeDefaults.WindowMinSize;
                     startUpMenu.Height = SizeDefaults.WindowMinSize;
-                    if (Settings.Gallery.IsBottomGalleryShown)
-                    {
-                        vm.GalleryWidth = SizeDefaults.WindowMinSize;
-                    }
+                    vm.PicViewer.GalleryWidth.Value = SizeDefaults.WindowMinSize;
                 }
                 }
-                vm.CurrentView = startUpMenu;
+
+                vm.MainWindow.CurrentView.Value = startUpMenu;
             }
             }
             
             
             TitleManager.SetNoImageTitle(vm);
             TitleManager.SetNoImageTitle(vm);
-
-            vm.GalleryMode = GalleryMode.Closed;
             GalleryFunctions.Clear();
             GalleryFunctions.Clear();
             MenuManager.CloseMenus(vm);
             MenuManager.CloseMenus(vm);
-            vm.GalleryMargin = new Thickness(0, 0, 0, 0);
-            vm.GetIndex = 0;
+
+            vm.PicViewer.GetIndex.Value = 0;
             vm.PlatformService.StopTaskbarProgress();
             vm.PlatformService.StopTaskbarProgress();
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
 
 
             _ = NavigationManager.DisposeImageIteratorAsync();
             _ = NavigationManager.DisposeImageIteratorAsync();
             if (UIHelper.GetEditableTitlebar is not null)
             if (UIHelper.GetEditableTitlebar is not null)
             {
             {
                 UIHelper.GetEditableTitlebar.TextBlock.TextAlignment = TextAlignment.Center;
                 UIHelper.GetEditableTitlebar.TextBlock.TextAlignment = TextAlignment.Center;
             }
             }
+            if (vm.Gallery is null)
+            {
+                return;
+            }
+            vm.Gallery.GalleryMode.Value = GalleryMode.Closed;
+            vm.Gallery.GalleryMargin.Value = new Thickness(0, 0, 0, 0);
         }
         }
     }
     }
 
 
     public static async Task ReloadAsync(MainViewModel vm)
     public static async Task ReloadAsync(MainViewModel vm)
     {
     {
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         
         
         if (vm.PicViewer.ImageSource is null)
         if (vm.PicViewer.ImageSource is null)
         {
         {
@@ -99,7 +101,7 @@ public static class ErrorHandling
         {
         {
             if (!NavigationManager.CanNavigate(vm))
             if (!NavigationManager.CanNavigate(vm))
             {
             {
-                var url = vm.PicViewer.Title.GetURL();
+                var url = vm.PicViewer.Title.CurrentValue.GetURL();
                 if (!string.IsNullOrEmpty(url))
                 if (!string.IsNullOrEmpty(url))
                 {
                 {
                     await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
                     await NavigationManager.LoadPicFromUrlAsync(url, vm).ConfigureAwait(false);
@@ -123,7 +125,7 @@ public static class ErrorHandling
         }
         }
         finally
         finally
         {
         {
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         }
         }
     }
     }
 }
 }

+ 75 - 66
src/PicView.Avalonia/Navigation/ExifHandling.cs

@@ -13,7 +13,7 @@ public static class ExifHandling
 {
 {
     public static void UpdateExifValues(MainViewModel vm)
     public static void UpdateExifValues(MainViewModel vm)
     {
     {
-        if (vm.PicViewer?.FileInfo is null || vm.PicViewer is { PixelWidth: <= 0, PixelHeight: <= 0 })
+        if (vm.PicViewer?.FileInfo is null || vm.PicViewer is { PixelWidth.CurrentValue: <= 0, PixelHeight.CurrentValue: <= 0 })
         {
         {
             return;
             return;
         }
         }
@@ -21,39 +21,39 @@ public static class ExifHandling
         
         
         try
         try
         {
         {
-            if (!vm.PicViewer.FileInfo.Exists)
+            if (!vm.PicViewer.FileInfo.CurrentValue.Exists)
             {
             {
                 return;
                 return;
             }
             }
-            magick.Ping(vm.PicViewer.FileInfo);
+            magick.Ping(vm.PicViewer.FileInfo.CurrentValue);
             var profile = magick.GetExifProfile();
             var profile = magick.GetExifProfile();
 
 
             if (profile != null)
             if (profile != null)
             {
             {
-                vm.Exif.DpiY = profile?.GetValue(ExifTag.YResolution)?.Value.ToDouble() ?? 0;
-                vm.Exif.DpiX = profile?.GetValue(ExifTag.XResolution)?.Value.ToDouble() ?? 0;
+                vm.Exif.DpiY.Value = profile?.GetValue(ExifTag.YResolution)?.Value.ToDouble() ?? 0;
+                vm.Exif.DpiX.Value = profile?.GetValue(ExifTag.XResolution)?.Value.ToDouble() ?? 0;
                 var depth = profile?.GetValue(ExifTag.BitsPerSample)?.Value;
                 var depth = profile?.GetValue(ExifTag.BitsPerSample)?.Value;
                 if (depth is not null)
                 if (depth is not null)
                 {
                 {
                     var x = depth.Aggregate(0, (current, value) => current + value);
                     var x = depth.Aggregate(0, (current, value) => current + value);
-                    vm.Exif.BitDepth = x.ToString();
+                    vm.Exif.BitDepth.Value = x.ToString();
                 }
                 }
                 else
                 else
                 {
                 {
-                    vm.Exif.BitDepth = (magick.Depth * 3).ToString();
+                    vm.Exif.BitDepth.Value = (magick.Depth * 3).ToString();
                 }
                 }
             }
             }
 
 
-            if (vm.Exif.DpiX is 0 && vm.PicViewer.ImageType is ImageType.Bitmap or ImageType.AnimatedGif or ImageType.AnimatedWebp)
+            if (vm.Exif.DpiX.CurrentValue is 0 && vm.PicViewer.ImageType.CurrentValue is ImageType.Bitmap or ImageType.AnimatedGif or ImageType.AnimatedWebp)
             {
             {
-                if (vm.PicViewer.ImageSource is Bitmap bmp)
+                if (vm.PicViewer.ImageSource.CurrentValue is Bitmap bmp)
                 {
                 {
-                    vm.Exif.DpiX = bmp?.Dpi.X ?? 0;
-                    vm.Exif.DpiY = bmp?.Dpi.Y ?? 0;
+                    vm.Exif.DpiX.Value = bmp?.Dpi.X ?? 0;
+                    vm.Exif.DpiY.Value = bmp?.Dpi.Y ?? 0;
                 }
                 }
             }
             }
 
 
-            vm.Exif.Orientation = vm.PicViewer.ExifOrientation switch
+            vm.Exif.Orientation.Value = vm.PicViewer.ExifOrientation.CurrentValue switch
             {
             {
                 EXIFHelper.EXIFOrientation.Horizontal => TranslationManager.Translation.Normal,
                 EXIFHelper.EXIFOrientation.Horizontal => TranslationManager.Translation.Normal,
                 EXIFHelper.EXIFOrientation.MirrorHorizontal => TranslationManager.Translation.Flipped,
                 EXIFHelper.EXIFOrientation.MirrorHorizontal => TranslationManager.Translation.Flipped,
@@ -71,93 +71,102 @@ public static class ExifHandling
 
 
             var meter = TranslationManager.Translation.Meter;
             var meter = TranslationManager.Translation.Meter;
 
 
-            if (string.IsNullOrEmpty(vm.Exif.BitDepth))
+            if (string.IsNullOrEmpty(vm.Exif.BitDepth.CurrentValue))
             {
             {
-                vm.Exif.BitDepth = (magick.Depth * 3).ToString();
+                vm.Exif.BitDepth.Value = (magick.Depth * 3).ToString();
             }
             }
 
 
-            if (vm.Exif.DpiX == 0 || vm.Exif.DpiY == 0) // Check for zero before division
+            if (vm.Exif.DpiX.CurrentValue == 0 || vm.Exif.DpiY.CurrentValue == 0) // Check for zero before division
             {
             {
-                vm.Exif.PrintSizeCm = vm.Exif.PrintSizeInch = vm.Exif.SizeMp = vm.Exif.Resolution = string.Empty;
+                vm.Exif.PrintSizeCm.Value =
+                    vm.Exif.PrintSizeInch.Value =
+                        vm.Exif.SizeMp.Value =
+                            vm.Exif.Resolution.Value = string.Empty;
             }
             }
             else 
             else 
             {
             {
-                var printSizes = AspectRatioHelper.GetPrintSizes( vm.PicViewer.PixelWidth, vm.PicViewer.PixelHeight, vm.Exif.DpiX, vm.Exif.DpiY);
+                var printSizes = AspectRatioHelper.GetPrintSizes(vm.PicViewer.PixelWidth.CurrentValue,
+                    vm.PicViewer.PixelHeight.CurrentValue,
+                    vm.Exif.DpiX.CurrentValue,
+                    vm.Exif.DpiY.CurrentValue);
 
 
-                vm.Exif.PrintSizeCm = printSizes.PrintSizeCm;
-                vm.Exif.PrintSizeInch = printSizes.PrintSizeInch;
-                vm.Exif.SizeMp = printSizes.SizeMp;
+                vm.Exif.PrintSizeCm.Value = printSizes.PrintSizeCm;
+                vm.Exif.PrintSizeInch.Value = printSizes.PrintSizeInch;
+                vm.Exif.SizeMp.Value = printSizes.SizeMp;
 
 
-                vm.Exif.Resolution = $"{vm.Exif.DpiX} x {vm.Exif.DpiY} {TranslationManager.Translation.Dpi}";
+                vm.Exif.Resolution.Value = $"{vm.Exif.DpiX} x {vm.Exif.DpiY} {TranslationManager.Translation.Dpi}";
             }
             }
 
 
-            var gcd = ImageTitleFormatter.GCD(vm.PicViewer.PixelWidth, vm.PicViewer.PixelHeight);
+            var gcd = ImageTitleFormatter.GCD(vm.PicViewer.PixelWidth.CurrentValue, vm.PicViewer.PixelHeight.CurrentValue);
             if (gcd != 0) // Check for zero before division
             if (gcd != 0) // Check for zero before division
             {
             {
-                vm.Exif.AspectRatio = AspectRatioHelper.GetFormattedAspectRatio(gcd, vm.PicViewer.PixelWidth, vm.PicViewer.PixelHeight);
+                vm.Exif.AspectRatio.Value = AspectRatioHelper.GetFormattedAspectRatio(gcd, vm.PicViewer.PixelWidth.CurrentValue, vm.PicViewer.PixelHeight.CurrentValue);
             }
             }
             else
             else
             {
             {
-                vm.Exif.AspectRatio = string.Empty; // Handle cases where gcd is 0
+                vm.Exif.AspectRatio.Value = string.Empty; // Handle cases where gcd is 0
             }
             }
 
 
-            vm.EXIFRating = profile?.GetValue(ExifTag.Rating)?.Value ?? 0;
+            vm.Exif.ExifRating.Value = profile?.GetValue(ExifTag.Rating)?.Value ?? 0;
 
 
             var gpsValues = EXIFHelper.GetGPSValues(profile);
             var gpsValues = EXIFHelper.GetGPSValues(profile);
 
 
             if (gpsValues is not null)
             if (gpsValues is not null)
             {
             {
-                vm.Exif.Latitude = gpsValues[0];
-                vm.Exif.Longitude = gpsValues[1];
+                vm.Exif.Latitude.Value = gpsValues[0];
+                vm.Exif.Longitude.Value = gpsValues[1];
 
 
-                vm.Exif.GoogleLink = gpsValues[2];
-                vm.Exif.BingLink = gpsValues[3];
+                vm.Exif.GoogleLink.Value = gpsValues[2];
+                vm.Exif.BingLink.Value = gpsValues[3];
             }
             }
             else
             else
             {
             {
-                vm.Exif.Latitude = vm.Exif.Longitude = vm.Exif.GoogleLink = vm.Exif.BingLink = string.Empty;
+                vm.Exif.Latitude.Value =
+                    vm.Exif.Longitude.Value =
+                        vm.Exif.GoogleLink.Value =
+                            vm.Exif.BingLink.Value = string.Empty;
             }
             }
 
 
             var altitude = profile?.GetValue(ExifTag.GPSAltitude)?.Value;
             var altitude = profile?.GetValue(ExifTag.GPSAltitude)?.Value;
-            vm.Exif.Altitude = altitude.HasValue
+            vm.Exif.Altitude.Value = altitude.HasValue
                 ? $"{altitude.Value.ToDouble()} {meter}"
                 ? $"{altitude.Value.ToDouble()} {meter}"
                 : string.Empty;
                 : string.Empty;
             var getAuthors = profile?.GetValue(ExifTag.Artist)?.Value;
             var getAuthors = profile?.GetValue(ExifTag.Artist)?.Value;
-            vm.Exif.Authors = getAuthors ?? string.Empty;
-            vm.Exif.DateTaken = EXIFHelper.GetDateTaken(profile);
-            vm.Exif.Copyright = profile?.GetValue(ExifTag.Copyright)?.Value ?? string.Empty;
-            vm.Exif.Title = EXIFHelper.GetTitle(profile);
-            vm.Exif.Subject = EXIFHelper.GetSubject(profile);
-            vm.Exif.Software = profile?.GetValue(ExifTag.Software)?.Value ?? string.Empty;
-            vm.Exif.ResolutionUnit = EXIFHelper.GetResolutionUnit(profile);
-            vm.Exif.ColorRepresentation = EXIFHelper.GetColorSpace(profile);
-            vm.Exif.Compression = profile?.GetValue(ExifTag.Compression)?.Value.ToString() ?? string.Empty;
-            vm.Exif.CompressedBitsPixel = profile?.GetValue(ExifTag.CompressedBitsPerPixel)?.Value.ToString() ?? string.Empty;
-            vm.Exif.CameraMaker = profile?.GetValue(ExifTag.Make)?.Value ?? string.Empty;
-            vm.Exif.CameraModel = profile?.GetValue(ExifTag.Model)?.Value ?? string.Empty;
-            vm.Exif.ExposureProgram = EXIFHelper.GetExposureProgram(profile);
-            vm.Exif.ExposureTime = profile?.GetValue(ExifTag.ExposureTime)?.Value.ToString() ?? string.Empty;
-            vm.Exif.FNumber = profile?.GetValue(ExifTag.FNumber)?.Value.ToString() ?? string.Empty;
-            vm.Exif.MaxAperture = profile?.GetValue(ExifTag.MaxApertureValue)?.Value.ToString() ?? string.Empty;
-            vm.Exif.ExposureBias = profile?.GetValue(ExifTag.ExposureBiasValue)?.Value.ToString() ?? string.Empty;
-            vm.Exif.DigitalZoom = profile?.GetValue(ExifTag.DigitalZoomRatio)?.Value.ToString() ?? string.Empty;
-            vm.Exif.FocalLength35Mm = profile?.GetValue(ExifTag.FocalLengthIn35mmFilm)?.Value.ToString() ?? string.Empty;
-            vm.Exif.FocalLength = profile?.GetValue(ExifTag.FocalLength)?.Value.ToString() ?? string.Empty;
-            vm.Exif.ISOSpeed = EXIFHelper.GetISOSpeed(profile);
-            vm.Exif.MeteringMode = profile?.GetValue(ExifTag.MeteringMode)?.Value.ToString() ?? string.Empty;
-            vm.Exif.Contrast = EXIFHelper.GetContrast(profile);
-            vm.Exif.Saturation = EXIFHelper.GetSaturation(profile);
-            vm.Exif.Sharpness = EXIFHelper.GetSharpness(profile);
-            vm.Exif.WhiteBalance = EXIFHelper.GetWhiteBalance(profile);
-            vm.Exif.FlashMode = EXIFHelper.GetFlashMode(profile);
-            vm.Exif.FlashEnergy = profile?.GetValue(ExifTag.FlashEnergy)?.Value.ToString() ?? string.Empty;
-            vm.Exif.LightSource = EXIFHelper.GetLightSource(profile);
-            vm.Exif.Brightness = profile?.GetValue(ExifTag.BrightnessValue)?.Value.ToString() ?? string.Empty;
-            vm.Exif.PhotometricInterpretation = EXIFHelper.GetPhotometricInterpretation(profile);
-            vm.Exif.ExifVersion = EXIFHelper.GetExifVersion(profile);
-            vm.Exif.LensModel = profile?.GetValue(ExifTag.LensModel)?.Value ?? string.Empty;
-            vm.Exif.LensMaker = profile?.GetValue(ExifTag.LensMake)?.Value ?? string.Empty;
-            vm.Exif.Comment = EXIFHelper.GetUserComment(profile);
+            vm.Exif.Authors.Value = getAuthors ?? string.Empty;
+            vm.Exif.DateTaken.Value = EXIFHelper.GetDateTaken(profile);
+            vm.Exif.Copyright.Value = profile?.GetValue(ExifTag.Copyright)?.Value ?? string.Empty;
+            vm.Exif.Title.Value = EXIFHelper.GetTitle(profile);
+            vm.Exif.Subject.Value = EXIFHelper.GetSubject(profile);
+            vm.Exif.Software.Value = profile?.GetValue(ExifTag.Software)?.Value ?? string.Empty;
+            vm.Exif.ResolutionUnit.Value = EXIFHelper.GetResolutionUnit(profile);
+            vm.Exif.ColorRepresentation.Value = EXIFHelper.GetColorSpace(profile);
+            vm.Exif.Compression.Value = profile?.GetValue(ExifTag.Compression)?.Value.ToString() ?? string.Empty;
+            vm.Exif.CompressedBitsPixel.Value = profile?.GetValue(ExifTag.CompressedBitsPerPixel)?.Value.ToString() ?? string.Empty;
+            vm.Exif.CameraMaker.Value = profile?.GetValue(ExifTag.Make)?.Value ?? string.Empty;
+            vm.Exif.CameraModel.Value = profile?.GetValue(ExifTag.Model)?.Value ?? string.Empty;
+            vm.Exif.ExposureProgram.Value = EXIFHelper.GetExposureProgram(profile);
+            vm.Exif.ExposureTime.Value = profile?.GetValue(ExifTag.ExposureTime)?.Value.ToString() ?? string.Empty;
+            vm.Exif.FNumber.Value = profile?.GetValue(ExifTag.FNumber)?.Value.ToString() ?? string.Empty;
+            vm.Exif.MaxAperture.Value = profile?.GetValue(ExifTag.MaxApertureValue)?.Value.ToString() ?? string.Empty;
+            vm.Exif.ExposureBias.Value = profile?.GetValue(ExifTag.ExposureBiasValue)?.Value.ToString() ?? string.Empty;
+            vm.Exif.DigitalZoom.Value = profile?.GetValue(ExifTag.DigitalZoomRatio)?.Value.ToString() ?? string.Empty;
+            vm.Exif.FocalLength35Mm.Value = profile?.GetValue(ExifTag.FocalLengthIn35mmFilm)?.Value.ToString() ?? string.Empty;
+            vm.Exif.FocalLength.Value = profile?.GetValue(ExifTag.FocalLength)?.Value.ToString() ?? string.Empty;
+            vm.Exif.ISOSpeed.Value = EXIFHelper.GetISOSpeed(profile);
+            vm.Exif.MeteringMode.Value = profile?.GetValue(ExifTag.MeteringMode)?.Value.ToString() ?? string.Empty;
+            vm.Exif.Contrast.Value = EXIFHelper.GetContrast(profile);
+            vm.Exif.Saturation.Value = EXIFHelper.GetSaturation(profile);
+            vm.Exif.Sharpness.Value = EXIFHelper.GetSharpness(profile);
+            vm.Exif.WhiteBalance.Value = EXIFHelper.GetWhiteBalance(profile);
+            vm.Exif.FlashMode.Value = EXIFHelper.GetFlashMode(profile);
+            vm.Exif.FlashEnergy.Value = profile?.GetValue(ExifTag.FlashEnergy)?.Value.ToString() ?? string.Empty;
+            vm.Exif.LightSource.Value = EXIFHelper.GetLightSource(profile);
+            vm.Exif.Brightness.Value = profile?.GetValue(ExifTag.BrightnessValue)?.Value.ToString() ?? string.Empty;
+            vm.Exif.PhotometricInterpretation.Value = EXIFHelper.GetPhotometricInterpretation(profile);
+            vm.Exif.ExifVersion.Value = EXIFHelper.GetExifVersion(profile);
+            vm.Exif.LensModel.Value = profile?.GetValue(ExifTag.LensModel)?.Value ?? string.Empty;
+            vm.Exif.LensMaker.Value = profile?.GetValue(ExifTag.LensMake)?.Value ?? string.Empty;
+            vm.Exif.Comment.Value = EXIFHelper.GetUserComment(profile);
         }
         }
         catch (Exception e)
         catch (Exception e)
         {
         {

+ 4 - 3
src/PicView.Avalonia/Navigation/FileListManager.cs

@@ -63,13 +63,14 @@ public static class FileListManager
         {
         {
             try
             try
             {
             {
-                var files = platformSpecificService.GetFiles(vm.PicViewer.FileInfo);
+                var files = platformSpecificService.GetFiles(vm.PicViewer.FileInfo.CurrentValue);
                 if (files is not { Count: > 0 })
                 if (files is not { Count: > 0 })
                 {
                 {
                     return false;
                     return false;
                 }
                 }
 
 
-                NavigationManager.UpdateFileListAndIndex(files, files.IndexOf(vm.PicViewer.FileInfo));
+                var index = files.FindIndex(info => info.FullName.Equals(vm.PicViewer.FileInfo.CurrentValue.FullName));
+                NavigationManager.UpdateFileListAndIndex(files, index);
                 TitleManager.SetTitle(vm);
                 TitleManager.SetTitle(vm);
                 return true;
                 return true;
             }
             }
@@ -89,7 +90,7 @@ public static class FileListManager
 
 
         if (!_cancellationTokenSource.IsCancellationRequested)
         if (!_cancellationTokenSource.IsCancellationRequested)
         {
         {
-            await GalleryLoad.ReloadGalleryAsync(vm, vm.PicViewer.FileInfo.DirectoryName);
+            await GalleryLoad.ReloadGalleryAsync(vm, vm.PicViewer.FileInfo.CurrentValue.DirectoryName);
         }
         }
     }
     }
 }
 }

+ 21 - 21
src/PicView.Avalonia/Navigation/ImageIterator.cs

@@ -130,7 +130,7 @@ public class ImageIterator : IAsyncDisposable
                 return; // Early exit
                 return; // Early exit
             }
             }
 
 
-            if (_vm.IsEditableTitlebarOpen)
+            if (_vm.MainWindow.IsEditableTitlebarOpen.CurrentValue)
             {
             {
                 // Don't react to changes when renaming
                 // Don't react to changes when renaming
                 return;
                 return;
@@ -153,7 +153,7 @@ public class ImageIterator : IAsyncDisposable
                 return; // Early exit
                 return; // Early exit
             }
             }
 
 
-            if (_vm.IsEditableTitlebarOpen)
+            if (_vm.MainWindow.IsEditableTitlebarOpen.Value)
             {
             {
                 // Don't react to changes when renaming
                 // Don't react to changes when renaming
                 return;
                 return;
@@ -176,7 +176,7 @@ public class ImageIterator : IAsyncDisposable
                 return; // Early exit
                 return; // Early exit
             }
             }
 
 
-            if (_vm.IsEditableTitlebarOpen)
+            if (_vm.MainWindow.IsEditableTitlebarOpen.CurrentValue)
             {
             {
                 // Don't react to changes when renaming
                 // Don't react to changes when renaming
                 return;
                 return;
@@ -232,9 +232,9 @@ public class ImageIterator : IAsyncDisposable
             {
             {
                 if (Settings.Gallery.IsBottomGalleryShown && ImagePaths.Count > 1)
                 if (Settings.Gallery.IsBottomGalleryShown && ImagePaths.Count > 1)
                 {
                 {
-                    if (_vm.GalleryMode is GalleryMode.BottomToClosed or GalleryMode.FullToClosed)
+                    if (_vm.Gallery.GalleryMode.CurrentValue is GalleryMode.BottomToClosed or GalleryMode.FullToClosed)
                     {
                     {
-                        _vm.GalleryMode = GalleryMode.ClosedToBottom;
+                        _vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
                     }
                     }
                 }
                 }
 
 
@@ -283,7 +283,7 @@ public class ImageIterator : IAsyncDisposable
                 PreLoader.Resynchronize(ImagePaths);
                 PreLoader.Resynchronize(ImagePaths);
                 var newIndex = GetIteration(index, NavigateTo.Previous);
                 var newIndex = GetIteration(index, NavigateTo.Previous);
                 CurrentIndex = newIndex;
                 CurrentIndex = newIndex;
-                _vm.PicViewer.FileInfo = ImagePaths[CurrentIndex];
+                _vm.PicViewer.FileInfo.Value = ImagePaths[CurrentIndex];
                 await IterateToIndex(CurrentIndex, new CancellationTokenSource());
                 await IterateToIndex(CurrentIndex, new CancellationTokenSource());
             }
             }
             else
             else
@@ -299,12 +299,12 @@ public class ImageIterator : IAsyncDisposable
                 {
                 {
                     if (ImagePaths.Count == 1)
                     if (ImagePaths.Count == 1)
                     {
                     {
-                        _vm.GalleryMode = GalleryMode.BottomToClosed;
+                        _vm.Gallery.GalleryMode.Value = GalleryMode.BottomToClosed;
                     }
                     }
                 }
                 }
 
 
-                var indexOf = ImagePaths.FindIndex(x => x.FullName.Equals(_vm.PicViewer.FileInfo.FullName));
-                _vm.SelectedGalleryItemIndex = indexOf; // Fixes deselection bug 
+                var indexOf = ImagePaths.FindIndex(x => x.FullName.Equals(_vm.PicViewer.FileInfo.CurrentValue.FullName));
+                _vm.PicViewer.Index.Value = indexOf; // Fixes deselection bug 
                 CurrentIndex = indexOf;
                 CurrentIndex = indexOf;
                 if (isSameFile)
                 if (isSameFile)
                 {
                 {
@@ -369,7 +369,7 @@ public class ImageIterator : IAsyncDisposable
 
 
             if (sameFile)
             if (sameFile)
             {
             {
-                _vm.PicViewer.FileInfo = newFileInfo;
+                _vm.PicViewer.FileInfo.Value = newFileInfo;
                 CurrentIndex = newIndex;
                 CurrentIndex = newIndex;
             }
             }
 
 
@@ -389,7 +389,7 @@ public class ImageIterator : IAsyncDisposable
                     _vm));
                     _vm));
             if (sameFile)
             if (sameFile)
             {
             {
-                _vm.SelectedGalleryItemIndex = newIndex;
+                _vm.PicViewer.Index.Value = newIndex;
                 GalleryFunctions.CenterGallery(_vm);
                 GalleryFunctions.CenterGallery(_vm);
             }
             }
         }
         }
@@ -449,12 +449,12 @@ public class ImageIterator : IAsyncDisposable
 
 
     public PreLoadValue? GetCurrentPreLoadValue() =>
     public PreLoadValue? GetCurrentPreLoadValue() =>
         _isRunning
         _isRunning
-            ? PreLoader.Get(_vm.PicViewer.FileInfo, ImagePaths)
+            ? PreLoader.Get(_vm.PicViewer.FileInfo.CurrentValue, ImagePaths)
             : PreLoader.Get(CurrentIndex, ImagePaths);
             : PreLoader.Get(CurrentIndex, ImagePaths);
 
 
     public async Task<PreLoadValue?> GetCurrentPreLoadValueAsync() =>
     public async Task<PreLoadValue?> GetCurrentPreLoadValueAsync() =>
         _isRunning
         _isRunning
-            ? await PreLoader.GetOrLoadAsync(_vm.PicViewer.FileInfo, ImagePaths)
+            ? await PreLoader.GetOrLoadAsync(_vm.PicViewer.FileInfo.CurrentValue, ImagePaths)
             : await PreLoader.GetOrLoadAsync(CurrentIndex, ImagePaths);
             : await PreLoader.GetOrLoadAsync(CurrentIndex, ImagePaths);
 
 
     public PreLoadValue? GetNextPreLoadValue()
     public PreLoadValue? GetNextPreLoadValue()
@@ -487,11 +487,11 @@ public class ImageIterator : IAsyncDisposable
         try
         try
         {
         {
             _isRunning = true;
             _isRunning = true;
-            var fileList = await Task.FromResult(_vm.PlatformService.GetFiles(_vm.PicViewer.FileInfo))
+            var fileList = await Task.FromResult(_vm.PlatformService.GetFiles(_vm.PicViewer.FileInfo.CurrentValue))
                 .ConfigureAwait(false);
                 .ConfigureAwait(false);
             var oldList = ImagePaths;
             var oldList = ImagePaths;
             ImagePaths = fileList;
             ImagePaths = fileList;
-            CurrentIndex = ImagePaths.FindIndex(x => x.FullName.Equals(_vm.PicViewer.FileInfo.FullName));
+            CurrentIndex = ImagePaths.FindIndex(x => x.FullName.Equals(_vm.PicViewer.FileInfo.CurrentValue.FullName));
             TitleManager.SetTitle(_vm);
             TitleManager.SetTitle(_vm);
             await ClearAsync().ConfigureAwait(false);
             await ClearAsync().ConfigureAwait(false);
             await PreloadAsync().ConfigureAwait(false);
             await PreloadAsync().ConfigureAwait(false);
@@ -788,7 +788,7 @@ public class ImageIterator : IAsyncDisposable
         {
         {
             if (index == CurrentIndex)
             if (index == CurrentIndex)
             {
             {
-                _vm.IsLoading = false;
+                _vm.MainWindow.IsLoadingIndicatorShown.Value = false;
             }
             }
         }
         }
 
 
@@ -798,7 +798,7 @@ public class ImageIterator : IAsyncDisposable
         {
         {
             TitleManager.SetLoadingTitle(_vm);
             TitleManager.SetLoadingTitle(_vm);
 
 
-            _vm.SelectedGalleryItemIndex = index;
+            _vm.PicViewer.Index.Value = index;
             if (Settings.Gallery.IsBottomGalleryShown)
             if (Settings.Gallery.IsBottomGalleryShown)
             {
             {
                 GalleryNavigation.CenterScrollToSelectedItem(_vm);
                 GalleryNavigation.CenterScrollToSelectedItem(_vm);
@@ -815,7 +815,7 @@ public class ImageIterator : IAsyncDisposable
             {
             {
                 if (thumb is not null)
                 if (thumb is not null)
                 {
                 {
-                    _vm.PicViewer.ImageSource = thumb;
+                    _vm.PicViewer.ImageSource.Value = thumb;
                 }
                 }
             }
             }
             else
             else
@@ -826,9 +826,9 @@ public class ImageIterator : IAsyncDisposable
                     return;
                     return;
                 }
                 }
 
 
-                _vm.PicViewer.ImageSource = thumb;
-                _vm.PicViewer.SecondaryImageSource = secondaryThumb;
-                _vm.IsLoading = thumb is null || secondaryThumb is null;
+                _vm.PicViewer.ImageSource.Value = thumb;
+                _vm.PicViewer.SecondaryImageSource.Value = secondaryThumb;
+                _vm.MainWindow.IsLoadingIndicatorShown.Value = thumb is null || secondaryThumb is null;
             }
             }
         }
         }
     }
     }

+ 26 - 26
src/PicView.Avalonia/Navigation/ImageLoader.cs

@@ -31,7 +31,7 @@ public static class ImageLoader
         }
         }
 
 
         MenuManager.CloseMenus(vm);
         MenuManager.CloseMenus(vm);
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         TitleManager.SetLoadingTitle(vm);
         TitleManager.SetLoadingTitle(vm);
 
 
         // Starting in new task makes it more responsive and works better
         // Starting in new task makes it more responsive and works better
@@ -42,7 +42,7 @@ public static class ImageLoader
             if (check == null)
             if (check == null)
             {
             {
                 await ErrorHandling.ReloadAsync(vm).ConfigureAwait(false);
                 await ErrorHandling.ReloadAsync(vm).ConfigureAwait(false);
-                vm.IsLoading = false;
+                vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                 ArchiveExtraction.Cleanup();
                 ArchiveExtraction.Cleanup();
                 return;
                 return;
             }
             }
@@ -50,37 +50,37 @@ public static class ImageLoader
             switch (check.Value.Type)
             switch (check.Value.Type)
             {
             {
                 case FileTypeResolver.LoadAbleFileType.File:
                 case FileTypeResolver.LoadAbleFileType.File:
-                    vm.CurrentView = vm.ImageViewer;
+                    vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                     await LoadPicFromFile(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
                     await LoadPicFromFile(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     ArchiveExtraction.Cleanup();
                     ArchiveExtraction.Cleanup();
                     return;
                     return;
                 case FileTypeResolver.LoadAbleFileType.Directory:
                 case FileTypeResolver.LoadAbleFileType.Directory:
-                    vm.CurrentView = vm.ImageViewer;
+                    vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                     await LoadPicFromDirectoryAsync(check.Value.Data, vm).ConfigureAwait(false);
                     await LoadPicFromDirectoryAsync(check.Value.Data, vm).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     ArchiveExtraction.Cleanup();
                     ArchiveExtraction.Cleanup();
                     return;
                     return;
                 case FileTypeResolver.LoadAbleFileType.Web:
                 case FileTypeResolver.LoadAbleFileType.Web:
-                    vm.CurrentView = vm.ImageViewer;
+                    vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                     await LoadPicFromUrlAsync(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
                     await LoadPicFromUrlAsync(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     ArchiveExtraction.Cleanup();
                     ArchiveExtraction.Cleanup();
                     return;
                     return;
                 case FileTypeResolver.LoadAbleFileType.Base64:
                 case FileTypeResolver.LoadAbleFileType.Base64:
-                    vm.CurrentView = vm.ImageViewer;
+                    vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                     await LoadPicFromBase64Async(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
                     await LoadPicFromBase64Async(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     ArchiveExtraction.Cleanup();
                     ArchiveExtraction.Cleanup();
                     return;
                     return;
                 case FileTypeResolver.LoadAbleFileType.Zip:
                 case FileTypeResolver.LoadAbleFileType.Zip:
-                    vm.CurrentView = vm.ImageViewer;
+                    vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                     await LoadPicFromArchiveAsync(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
                     await LoadPicFromArchiveAsync(check.Value.Data, vm, imageIterator).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     return;
                     return;
                 default:
                 default:
                     await ErrorHandling.ReloadAsync(vm).ConfigureAwait(false);
                     await ErrorHandling.ReloadAsync(vm).ConfigureAwait(false);
-                    vm.IsLoading = false;
+                    vm.MainWindow.IsLoadingIndicatorShown.Value = false;
                     ArchiveExtraction.Cleanup();
                     ArchiveExtraction.Cleanup();
                     return;
                     return;
             }
             }
@@ -121,7 +121,7 @@ public static class ImageLoader
                     await NavigationManager.CheckIfTiffAndUpdate(vm, fileInfo, index);
                     await NavigationManager.CheckIfTiffAndUpdate(vm, fileInfo, index);
                     if (Settings.Gallery.IsBottomGalleryShown && NavigationManager.GetCount > 0)
                     if (Settings.Gallery.IsBottomGalleryShown && NavigationManager.GetCount > 0)
                     {
                     {
-                        vm.GalleryMode = GalleryMode.ClosedToBottom;
+                        vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
                     }
                     }
                 }
                 }
                 else
                 else
@@ -164,7 +164,7 @@ public static class ImageLoader
     /// <param name="fileInfo">Optional: FileInfo object for the directory.</param>
     /// <param name="fileInfo">Optional: FileInfo object for the directory.</param>
     public static async Task LoadPicFromDirectoryAsync(string file, MainViewModel vm, FileInfo? fileInfo = null)
     public static async Task LoadPicFromDirectoryAsync(string file, MainViewModel vm, FileInfo? fileInfo = null)
     {
     {
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         TitleManager.SetLoadingTitle(vm);
         TitleManager.SetLoadingTitle(vm);
 
 
         if (_cancellationTokenSource is not null)
         if (_cancellationTokenSource is not null)
@@ -233,7 +233,7 @@ public static class ImageLoader
             await _cancellationTokenSource.CancelAsync().ConfigureAwait(false);
             await _cancellationTokenSource.CancelAsync().ConfigureAwait(false);
         }
         }
 
 
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         TitleManager.SetLoadingTitle(vm);
         TitleManager.SetLoadingTitle(vm);
 
 
         string? prevArchiveLocation = null;
         string? prevArchiveLocation = null;
@@ -333,9 +333,9 @@ public static class ImageLoader
                 var displayProgress = HttpManager.GetProgressDisplay(totalFileSize, totalBytesDownloaded,
                 var displayProgress = HttpManager.GetProgressDisplay(totalFileSize, totalBytesDownloaded,
                     progressPercentage);
                     progressPercentage);
                 var title = $"{fileName} {TranslationManager.Translation.Downloading} {displayProgress}";
                 var title = $"{fileName} {TranslationManager.Translation.Downloading} {displayProgress}";
-                vm.PicViewer.Title = title;
-                vm.PicViewer.TitleTooltip = title;
-                vm.PicViewer.WindowTitle = title;
+                vm.PicViewer.Title.Value = title;
+                vm.PicViewer.TitleTooltip.Value = title;
+                vm.PicViewer.WindowTitle.Value = title;
                 if (Settings.UIProperties.IsTaskbarProgressEnabled)
                 if (Settings.UIProperties.IsTaskbarProgressEnabled)
                 {
                 {
                     vm.PlatformService.SetTaskbarProgress((ulong)totalBytesDownloaded, (ulong)totalFileSize);
                     vm.PlatformService.SetTaskbarProgress((ulong)totalBytesDownloaded, (ulong)totalFileSize);
@@ -368,9 +368,9 @@ public static class ImageLoader
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
         await UpdateImage.SetSingleImageAsync(imageModel.Image, imageModel.ImageType, url, vm);
         await UpdateImage.SetSingleImageAsync(imageModel.Image, imageModel.ImageType, url, vm);
 
 
-        vm.IsLoading = false;
-        vm.PicViewer.FileInfo = fileInfo;
-        vm.PicViewer.ExifOrientation = imageModel.EXIFOrientation;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
+        vm.PicViewer.FileInfo.Value = fileInfo;
+        vm.PicViewer.ExifOrientation.Value = imageModel.EXIFOrientation;
         FileHistoryManager.Add(url);
         FileHistoryManager.Add(url);
 
 
         await NavigationManager.DisposeImageIteratorAsync();
         await NavigationManager.DisposeImageIteratorAsync();
@@ -390,9 +390,9 @@ public static class ImageLoader
     public static async Task LoadPicFromBase64Async(string base64, MainViewModel vm, ImageIterator imageIterator)
     public static async Task LoadPicFromBase64Async(string base64, MainViewModel vm, ImageIterator imageIterator)
     {
     {
         TitleManager.SetLoadingTitle(vm);
         TitleManager.SetLoadingTitle(vm);
-        vm.IsLoading = true;
-        vm.PicViewer.ImageSource = null;
-        vm.PicViewer.FileInfo = null;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
+        vm.PicViewer.ImageSource.Value = null;
+        vm.PicViewer.FileInfo.Value = null;
 
 
         if (_cancellationTokenSource is not null)
         if (_cancellationTokenSource is not null)
         {
         {
@@ -425,7 +425,7 @@ public static class ImageLoader
                 await ErrorHandling.ReloadAsync(vm);
                 await ErrorHandling.ReloadAsync(vm);
             }
             }
         });
         });
-        vm.IsLoading = false;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
     }
     }
 
 
     #endregion
     #endregion

+ 27 - 14
src/PicView.Avalonia/Navigation/NavigationManager.cs

@@ -45,14 +45,14 @@ public static class NavigationManager
     {
     {
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo).ConfigureAwait(false);
         ImageModel? nextImageModel = null;
         ImageModel? nextImageModel = null;
-        vm.PicViewer.ImageSource = imageModel.Image;
-        vm.PicViewer.ImageType = imageModel.ImageType;
+        vm.PicViewer.ImageSource.Value = imageModel.Image;
+        vm.PicViewer.ImageType.Value = imageModel.ImageType;
         if (!Settings.ImageScaling.ShowImageSideBySide)
         if (!Settings.ImageScaling.ShowImageSideBySide)
         {
         {
             await Dispatcher.UIThread.InvokeAsync(() =>
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
             {
                 vm.ImageViewer.SetTransform(imageModel.EXIFOrientation, imageModel.Format);
                 vm.ImageViewer.SetTransform(imageModel.EXIFOrientation, imageModel.Format);
-                WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, 0, 0, vm.RotationAngle,
+                WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, 0, 0, vm.GlobalSettings.RotationAngle.CurrentValue,
                     vm);
                     vm);
             });
             });
         }
         }
@@ -72,7 +72,7 @@ public static class NavigationManager
         if (Settings.ImageScaling.ShowImageSideBySide)
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
         {
             nextImageModel = (await ImageIterator.GetNextPreLoadValueAsync()).ImageModel;
             nextImageModel = (await ImageIterator.GetNextPreLoadValueAsync()).ImageModel;
-            vm.PicViewer.SecondaryImageSource = nextImageModel.Image;
+            vm.PicViewer.SecondaryImageSource.Value = nextImageModel.Image;
             await Dispatcher.UIThread.InvokeAsync(() =>
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
             {
                 WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, nextImageModel.PixelWidth,
                 WindowResizing.SetSize(imageModel.PixelWidth, imageModel.PixelHeight, nextImageModel.PixelWidth,
@@ -88,7 +88,7 @@ public static class NavigationManager
         }
         }
         else
         else
         {
         {
-            vm.IsSingleImage = false;
+            vm.PicViewer.IsSingleImage.Value = false;
             var isTiffUpdated = await CheckIfTiffAndUpdate(vm, fileInfo, index);
             var isTiffUpdated = await CheckIfTiffAndUpdate(vm, fileInfo, index);
             if (!isTiffUpdated)
             if (!isTiffUpdated)
             {
             {
@@ -110,7 +110,7 @@ public static class NavigationManager
             await WindowFunctions.ResizeAndFixRenderingError(vm);
             await WindowFunctions.ResizeAndFixRenderingError(vm);
         }
         }
 
 
-        vm.IsLoading = false;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         FileHistoryManager.Add(ImageIterator.ImagePaths[index].FullName);
         FileHistoryManager.Add(ImageIterator.ImagePaths[index].FullName);
         if (Settings.ImageScaling.ShowImageSideBySide)
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
         {
@@ -130,7 +130,7 @@ public static class NavigationManager
     public static bool CanNavigate(MainViewModel vm) =>
     public static bool CanNavigate(MainViewModel vm) =>
         ImageIterator?.ImagePaths is not null &&
         ImageIterator?.ImagePaths is not null &&
         ImageIterator.ImagePaths.Count > 0 && !CropFunctions.IsCropping &&
         ImageIterator.ImagePaths.Count > 0 && !CropFunctions.IsCropping &&
-        !DialogManager.IsDialogOpen && vm is { IsEditableTitlebarOpen: false, PicViewer.FileInfo: not null };
+        !DialogManager.IsDialogOpen && vm is { MainWindow.IsLoadingIndicatorShown.Value: false, PicViewer.FileInfo: not null };
 
 
     /// <summary>
     /// <summary>
     ///     Navigates to the next or previous image based on the <paramref name="next" /> parameter.
     ///     Navigates to the next or previous image based on the <paramref name="next" /> parameter.
@@ -145,7 +145,7 @@ public static class NavigationManager
             if (vm.PicViewer.FileInfo is null && ImageIterator is not null)
             if (vm.PicViewer.FileInfo is null && ImageIterator is not null)
             {
             {
                 // Fixes issue that shouldn't happen. Should investigate.
                 // Fixes issue that shouldn't happen. Should investigate.
-                vm.PicViewer.FileInfo = ImageIterator.ImagePaths[0];
+                vm.PicViewer.FileInfo.Value = ImageIterator.ImagePaths[0];
             }
             }
             else
             else
             {
             {
@@ -167,7 +167,7 @@ public static class NavigationManager
             if (vm.PicViewer.FileInfo is not null)
             if (vm.PicViewer.FileInfo is not null)
             {
             {
                 var newIndex =
                 var newIndex =
-                    ImageIterator.ImagePaths.FindIndex(x => x.FullName.Equals(vm.PicViewer.FileInfo.FullName));
+                    ImageIterator.ImagePaths.FindIndex(x => x.FullName.Equals(vm.PicViewer.FileInfo.CurrentValue.FullName));
                 if (newIndex == -1)
                 if (newIndex == -1)
                 {
                 {
                     ErrorHandling.ShowStartUpMenu(vm);
                     ErrorHandling.ShowStartUpMenu(vm);
@@ -248,7 +248,7 @@ public static class NavigationManager
             else
             else
             {
             {
                 await UpdateImage.SetTiffImageAsync(TiffNavigationInfo, ImageIterator.CurrentIndex,
                 await UpdateImage.SetTiffImageAsync(TiffNavigationInfo, ImageIterator.CurrentIndex,
-                    vm.PicViewer.FileInfo, vm);
+                    vm.PicViewer.FileInfo.CurrentValue, vm);
             }
             }
         }
         }
 
 
@@ -311,8 +311,11 @@ public static class NavigationManager
 
 
         await ImageLoader.CheckCancellationAndStartIterateToIndex(index, ImageIterator).ConfigureAwait(false);
         await ImageLoader.CheckCancellationAndStartIterateToIndex(index, ImageIterator).ConfigureAwait(false);
     }
     }
+    
+    public static async Task NavigateIncrements(bool next, bool is10, bool is100) =>
+        await NavigateIncrements(UIHelper.GetMainView.DataContext as MainViewModel, next, is10, is100).ConfigureAwait(false);
 
 
-    private static async Task NavigateIncrements(MainViewModel vm, bool next, bool is10, bool is100)
+    public static async Task NavigateIncrements(MainViewModel vm, bool next, bool is10, bool is100)
     {
     {
         if (!CanNavigate(vm))
         if (!CanNavigate(vm))
         {
         {
@@ -362,6 +365,10 @@ public static class NavigationManager
             await UIHelper.ScrollToEndIfNecessary(last);
             await UIHelper.ScrollToEndIfNecessary(last);
         }
         }
     }
     }
+    
+    /// <inheritdoc cref="NavigateBetweenDirectories(bool last, MainViewModel vm)"/>
+    public static async Task NavigateFirstOrLast(bool last) =>
+        await NavigateBetweenDirectories(last, UIHelper.GetMainView.DataContext as MainViewModel);
 
 
     /// <summary>
     /// <summary>
     ///     Iterates to the next or previous image based on the <paramref name="next" /> parameter.
     ///     Iterates to the next or previous image based on the <paramref name="next" /> parameter.
@@ -380,6 +387,7 @@ public static class NavigationManager
             await Navigate(next, vm);
             await Navigate(next, vm);
         }
         }
     }
     }
+    public static async Task Iterate(bool next) => await Iterate(next, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
 
 
     /// <summary>
     /// <summary>
     ///     Navigates and moves the cursor to the corresponding button.
     ///     Navigates and moves the cursor to the corresponding button.
@@ -402,7 +410,7 @@ public static class NavigationManager
         else
         else
         {
         {
             await Navigate(next, vm);
             await Navigate(next, vm);
-            await UIHelper.MoveCursorOnButtonClick(next, arrow, vm);
+            UIHelper.MoveCursorOnButtonClick(next, arrow, vm);
         }
         }
     }
     }
 
 
@@ -446,7 +454,7 @@ public static class NavigationManager
             {
             {
                 vm.PlatformService.StopTaskbarProgress();
                 vm.PlatformService.StopTaskbarProgress();
                 await LoadWithoutImageIterator(fileList[0], vm, fileList);
                 await LoadWithoutImageIterator(fileList[0], vm, fileList);
-                if (vm.PicViewer.Title == TranslationManager.Translation.Loading)
+                if (vm.PicViewer.Title.CurrentValue == TranslationManager.Translation.Loading)
                 {
                 {
                     TitleManager.SetTitle(vm);
                     TitleManager.SetTitle(vm);
                 }
                 }
@@ -484,6 +492,11 @@ public static class NavigationManager
             });
             });
         }
         }
     }
     }
+    
+    /// <inheritdoc cref="NavigateBetweenDirectories(bool next, MainViewModel vm)"/>
+    public static async Task NavigateBetweenDirectories(bool next) =>
+        await NavigateBetweenDirectories(next, UIHelper.GetMainView.DataContext as MainViewModel);
+        
 
 
     /// <summary>
     /// <summary>
     ///     Gets the list of files in the next or previous folder.
     ///     Gets the list of files in the next or previous folder.
@@ -570,7 +583,7 @@ public static class NavigationManager
 
 
     public static void InitializeImageIterator(MainViewModel vm)
     public static void InitializeImageIterator(MainViewModel vm)
     {
     {
-        ImageIterator ??= new ImageIterator(vm.PicViewer.FileInfo, vm);
+        ImageIterator ??= new ImageIterator(vm.PicViewer.FileInfo.CurrentValue, vm);
     }
     }
 
 
     public static async Task DisposeImageIteratorAsync()
     public static async Task DisposeImageIteratorAsync()

+ 2 - 2
src/PicView.Avalonia/Navigation/Slideshow.cs

@@ -58,7 +58,7 @@ public static class Slideshow
 
 
         if (Settings.Gallery.IsBottomGalleryShown)
         if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.ClosedToBottom;
+            vm.Gallery.GalleryMode.Value = GalleryMode.ClosedToBottom;
         }
         }
         
         
         _timer.Stop();
         _timer.Stop();
@@ -118,7 +118,7 @@ public static class Slideshow
 
 
         if (GalleryFunctions.IsFullGalleryOpen || Settings.Gallery.IsBottomGalleryShown)
         if (GalleryFunctions.IsFullGalleryOpen || Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.BottomToClosed;
+            vm.Gallery.GalleryMode.Value = GalleryMode.BottomToClosed;
         }
         }
     }
     }
 }
 }

+ 41 - 44
src/PicView.Avalonia/Navigation/UpdateImage.cs

@@ -83,11 +83,11 @@ public static class UpdateImage
 
 
             if (Settings.ImageScaling.ShowImageSideBySide && nextPreloadValue is { ImageModel: not null })
             if (Settings.ImageScaling.ShowImageSideBySide && nextPreloadValue is { ImageModel: not null })
             {
             {
-                vm.PicViewer.SecondaryImageSource = nextPreloadValue.ImageModel.Image;
+                vm.PicViewer.SecondaryImageSource.Value = nextPreloadValue.ImageModel.Image;
                 if (preLoadValue is { ImageModel: not null})
                 if (preLoadValue is { ImageModel: not null})
                 {
                 {
-                    vm.PicViewer.ImageSource = preLoadValue.ImageModel.Image;
-                    vm.PicViewer.ImageType = preLoadValue.ImageModel.ImageType;
+                    vm.PicViewer.ImageSource.Value = preLoadValue.ImageModel.Image;
+                    vm.PicViewer.ImageType.Value = preLoadValue.ImageModel.ImageType;
                 }
                 }
             }
             }
             else if (preLoadValue is { ImageModel: not null})
             else if (preLoadValue is { ImageModel: not null})
@@ -97,9 +97,9 @@ public static class UpdateImage
                     vm.ImageViewer.MainImage.InitialAnimatedSource = preLoadValue.ImageModel.FileInfo.FullName;
                     vm.ImageViewer.MainImage.InitialAnimatedSource = preLoadValue.ImageModel.FileInfo.FullName;
                 }
                 }
                 
                 
-                vm.PicViewer.ImageSource = preLoadValue.ImageModel.Image;
-                vm.PicViewer.SecondaryImageSource = null;
-                vm.PicViewer.ImageType = preLoadValue.ImageModel.ImageType;
+                vm.PicViewer.ImageSource.Value = preLoadValue.ImageModel.Image;
+                vm.PicViewer.SecondaryImageSource.Value = null;
+                vm.PicViewer.ImageType.Value = preLoadValue.ImageModel.ImageType;
             }
             }
             else
             else
             {
             {
@@ -114,7 +114,7 @@ public static class UpdateImage
             UIHelper.GetToolTipMessage.IsVisible = false;
             UIHelper.GetToolTipMessage.IsVisible = false;
         }, DispatcherPriority.Send);
         }, DispatcherPriority.Send);
 
 
-        vm.IsLoading = false;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
 
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
         {
@@ -154,13 +154,10 @@ public static class UpdateImage
             await Dispatcher.UIThread.InvokeAsync(() => { WindowFunctions.CenterWindowOnScreen(); });
             await Dispatcher.UIThread.InvokeAsync(() => { WindowFunctions.CenterWindowOnScreen(); });
         }
         }
 
 
-        if (vm.SelectedGalleryItemIndex != index)
+        vm.PicViewer.Index.Value = index;
+        if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.SelectedGalleryItemIndex = index;
-            if (Settings.Gallery.IsBottomGalleryShown)
-            {
-                GalleryNavigation.CenterScrollToSelectedItem(vm);
-            }
+            GalleryNavigation.CenterScrollToSelectedItem(vm);
         }
         }
         
         
         SetStats(vm, preLoadValue.ImageModel);
         SetStats(vm, preLoadValue.ImageModel);
@@ -171,7 +168,7 @@ public static class UpdateImage
         {
         {
             WindowResizing.SetSize(preLoadValue.ImageModel.PixelWidth, preLoadValue.ImageModel.PixelHeight,
             WindowResizing.SetSize(preLoadValue.ImageModel.PixelWidth, preLoadValue.ImageModel.PixelHeight,
                 nextPreloadValue?.ImageModel?.PixelWidth ?? 0, nextPreloadValue?.ImageModel?.PixelHeight ?? 0,
                 nextPreloadValue?.ImageModel?.PixelWidth ?? 0, nextPreloadValue?.ImageModel?.PixelHeight ?? 0,
-                vm.RotationAngle, vm);
+                vm.GlobalSettings.RotationAngle.CurrentValue, vm);
         }
         }
 
 
     }
     }
@@ -191,24 +188,24 @@ public static class UpdateImage
         MainViewModel vm)
         MainViewModel vm)
     {
     {
         var source = await Task.Run( () => tiffNavigationInfo.Pages[tiffNavigationInfo.CurrentPage].ToWriteableBitmap()).ConfigureAwait(false);
         var source = await Task.Run( () => tiffNavigationInfo.Pages[tiffNavigationInfo.CurrentPage].ToWriteableBitmap()).ConfigureAwait(false);
-        vm.PicViewer.ImageSource = source;
-        vm.PicViewer.SecondaryImageSource = null;
-        vm.PicViewer.ImageType = ImageType.Bitmap;
+        vm.PicViewer.ImageSource.Value = source;
+        vm.PicViewer.SecondaryImageSource.Value = null;
+        vm.PicViewer.ImageType.Value = ImageType.Bitmap;
         var width = source?.PixelSize.Width ?? 0;
         var width = source?.PixelSize.Width ?? 0;
         var height = source?.PixelSize.Height ?? 0;
         var height = source?.PixelSize.Height ?? 0;
         
         
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
-            if (vm.CurrentView != vm.ImageViewer)
+            if (vm.MainWindow.CurrentView.CurrentValue != vm.ImageViewer)
             {
             {
-                vm.CurrentView = vm.ImageViewer;
+                vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             }
             }
             
             
             WindowResizing.SetSize(width, height, 0, 0, 0, vm);
             WindowResizing.SetSize(width, height, 0, 0, 0, vm);
             
             
-            if (vm.RotationAngle != 0)
+            if (vm.GlobalSettings.RotationAngle.CurrentValue != 0)
             {
             {
-                vm.ImageViewer.Rotate(vm.RotationAngle);
+                vm.ImageViewer.Rotate(vm.GlobalSettings.RotationAngle.CurrentValue);
             }
             }
         }, DispatcherPriority.Render);
         }, DispatcherPriority.Render);
         
         
@@ -251,48 +248,48 @@ public static class UpdateImage
             var path = source as string;
             var path = source as string;
             using var magickImage = new MagickImage();
             using var magickImage = new MagickImage();
             magickImage.Ping(path);
             magickImage.Ping(path);
-            vm.PicViewer.ImageSource = source;
-            vm.PicViewer.ImageType = ImageType.Svg;
+            vm.PicViewer.ImageSource.Value = source;
+            vm.PicViewer.ImageType.Value = ImageType.Svg;
             width = (int)magickImage.Width;
             width = (int)magickImage.Width;
             height = (int)magickImage.Height;
             height = (int)magickImage.Height;
         }
         }
         else
         else
         {
         {
             var bitmap = source as Bitmap;
             var bitmap = source as Bitmap;
-            vm.PicViewer.ImageSource = source;
-            vm.PicViewer.ImageType = imageType == ImageType.Invalid ? ImageType.Bitmap : imageType;
+            vm.PicViewer.ImageSource.Value = source;
+            vm.PicViewer.ImageType.Value = imageType == ImageType.Invalid ? ImageType.Bitmap : imageType;
             width = bitmap?.PixelSize.Width ?? 0;
             width = bitmap?.PixelSize.Width ?? 0;
             height = bitmap?.PixelSize.Height ?? 0;
             height = bitmap?.PixelSize.Height ?? 0;
         }
         }
 
 
-        vm.PicViewer.FileInfo = null;
+        vm.PicViewer.FileInfo.Value = null;
 
 
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
-            if (vm.CurrentView != vm.ImageViewer)
+            if (vm.MainWindow.CurrentView.CurrentValue != vm.ImageViewer)
             {
             {
-                vm.CurrentView = vm.ImageViewer;
+                vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             }
             }
             WindowResizing.SetSize(width, height, 0, 0, 0, vm);
             WindowResizing.SetSize(width, height, 0, 0, 0, vm);
         }, DispatcherPriority.Send);
         }, DispatcherPriority.Send);
 
 
         var singeImageWindowTitles = ImageTitleFormatter.GenerateTitleForSingleImage(width, height, name, 1);
         var singeImageWindowTitles = ImageTitleFormatter.GenerateTitleForSingleImage(width, height, name, 1);
-        vm.PicViewer.WindowTitle = singeImageWindowTitles.TitleWithAppName;
-        vm.PicViewer.Title = singeImageWindowTitles.BaseTitle;
-        vm.PicViewer.TitleTooltip = singeImageWindowTitles.BaseTitle;
+        vm.PicViewer.WindowTitle.Value = singeImageWindowTitles.TitleWithAppName;
+        vm.PicViewer.Title.Value = singeImageWindowTitles.BaseTitle;
+        vm.PicViewer.TitleTooltip.Value = singeImageWindowTitles.BaseTitle;
 
 
         vm.PlatformService.StopTaskbarProgress();
         vm.PlatformService.StopTaskbarProgress();
 
 
-        vm.PicViewer.PixelWidth = width;
-        vm.PicViewer.PixelHeight = height;
+        vm.PicViewer.PixelWidth.Value = width;
+        vm.PicViewer.PixelHeight.Value = height;
 
 
         if (Settings.Gallery.IsBottomGalleryShown)
         if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
-            vm.GalleryMode = GalleryMode.Closed;
-            vm.GalleryMargin = new Thickness(0);
+            vm.Gallery.GalleryMode.Value = GalleryMode.Closed;
+            vm.Gallery.GalleryMargin.Value = new Thickness(0);
         }
         }
 
 
-        vm.IsSingleImage = true;
+        vm.PicViewer.IsSingleImage.Value = true;
         await Dispatcher.UIThread.InvokeAsync(() => { UIHelper.GetGalleryView.IsVisible = false; }, DispatcherPriority.Render);
         await Dispatcher.UIThread.InvokeAsync(() => { UIHelper.GetGalleryView.IsVisible = false; }, DispatcherPriority.Render);
         await NavigationManager.DisposeImageIteratorAsync();
         await NavigationManager.DisposeImageIteratorAsync();
     }
     }
@@ -303,13 +300,13 @@ public static class UpdateImage
 
 
     public static void SetStats(MainViewModel vm, ImageModel imageModel)
     public static void SetStats(MainViewModel vm, ImageModel imageModel)
     {
     {
-        vm.IsSingleImage = false;
-        vm.PicViewer.PixelWidth = imageModel.PixelWidth;
-        vm.PicViewer.PixelHeight = imageModel.PixelHeight;
-        vm.GetIndex = NavigationManager.GetNonZeroIndex;
-        vm.PicViewer.ExifOrientation = imageModel.EXIFOrientation;
-        vm.PicViewer.FileInfo = imageModel.FileInfo;
-        vm.ZoomValue = 1;
+        vm.PicViewer.IsSingleImage.Value = false;
+        vm.PicViewer.PixelWidth.Value = imageModel.PixelWidth;
+        vm.PicViewer.PixelHeight.Value = imageModel.PixelHeight;
+        vm.PicViewer.GetIndex.Value = NavigationManager.GetNonZeroIndex;
+        vm.PicViewer.ExifOrientation.Value = imageModel.EXIFOrientation;
+        vm.PicViewer.FileInfo.Value = imageModel.FileInfo;
+        vm.GlobalSettings.ZoomValue.Value = 1;
 
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         if (Settings.ImageScaling.ShowImageSideBySide)
         {
         {
@@ -319,7 +316,7 @@ public static class UpdateImage
         }
         }
 
 
         // Reset effects
         // Reset effects
-        vm.PicViewer.EffectConfig = null;
+        vm.PicViewer.EffectConfig.Value = null;
     }
     }
 
 
     #endregion
     #endregion

+ 1 - 1
src/PicView.Avalonia/PicView.Avalonia.csproj

@@ -126,10 +126,10 @@
   <ItemGroup>
   <ItemGroup>
     <PackageReference Include="Avalonia" Version="11.3.2" />
     <PackageReference Include="Avalonia" Version="11.3.2" />
     <PackageReference Include="Avalonia.Desktop" Version="11.3.2" />
     <PackageReference Include="Avalonia.Desktop" Version="11.3.2" />
-    <PackageReference Include="Avalonia.ReactiveUI" Version="11.3.2" />
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
     <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
     <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.2" />
     <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.2" />
     <PackageReference Include="Avalonia.Svg.Skia" Version="11.2.7" />
     <PackageReference Include="Avalonia.Svg.Skia" Version="11.2.7" />
+    <PackageReference Include="R3Extensions.Avalonia" Version="1.3.0" />
   </ItemGroup>
   </ItemGroup>
 
 
   <ItemGroup>
   <ItemGroup>

+ 2 - 2
src/PicView.Avalonia/Resizing/AspectRatioHelper.cs

@@ -14,8 +14,8 @@ public static class AspectRatioHelper
         var percentage = isWidth ? widthTextBox.Text.GetPercentage() : heightTextBox.Text.GetPercentage();
         var percentage = isWidth ? widthTextBox.Text.GetPercentage() : heightTextBox.Text.GetPercentage();
         if (percentage > 0)
         if (percentage > 0)
         {
         {
-            var newWidth = Convert.ToUInt32(vm.PicViewer.PixelWidth * (percentage / 100));
-            var newHeight = Convert.ToUInt32(vm.PicViewer.PixelHeight * (percentage / 100));
+            var newWidth = Convert.ToUInt32(vm.PicViewer.PixelWidth.CurrentValue * (percentage / 100));
+            var newHeight = Convert.ToUInt32(vm.PicViewer.PixelHeight.CurrentValue * (percentage / 100));
 
 
             widthTextBox.Text = newWidth.ToString("# ", CultureInfo.CurrentCulture);
             widthTextBox.Text = newWidth.ToString("# ", CultureInfo.CurrentCulture);
             heightTextBox.Text = newHeight.ToString("# ", CultureInfo.CurrentCulture);
             heightTextBox.Text = newHeight.ToString("# ", CultureInfo.CurrentCulture);

+ 9 - 9
src/PicView.Avalonia/SettingsManagement/LanguageUpdater.cs

@@ -18,34 +18,34 @@ public static class LanguageUpdater
 
 
         translationViewModel.UpdateLanguage();
         translationViewModel.UpdateLanguage();
 
 
-        translationViewModel.IsFlipped = picViewerModel.ScaleX == 1 ? translationViewModel.Flip : translationViewModel.UnFlip;
+        translationViewModel.IsFlipped.Value = picViewerModel.ScaleX.CurrentValue == 1 ? translationViewModel.Flip.CurrentValue : translationViewModel.UnFlip.CurrentValue;
         
         
-        translationViewModel.IsShowingUI = !Settings.UIProperties.ShowInterface ? translationViewModel.ShowUI : translationViewModel.HideUI;
+        translationViewModel.IsShowingUI.Value = !Settings.UIProperties.ShowInterface ? translationViewModel.ShowUI.CurrentValue : translationViewModel.HideUI.CurrentValue;
         
         
-        translationViewModel.IsScrolling = Settings.Zoom.ScrollEnabled ?
+        translationViewModel.IsScrolling.Value = Settings.Zoom.ScrollEnabled ?
             TranslationManager.Translation.ScrollingEnabled : TranslationManager.Translation.ScrollingDisabled;
             TranslationManager.Translation.ScrollingEnabled : TranslationManager.Translation.ScrollingDisabled;
         
         
-        translationViewModel.IsShowingBottomGallery = Settings.Gallery.IsBottomGalleryShown ?
+        translationViewModel.IsShowingBottomGallery.Value = Settings.Gallery.IsBottomGalleryShown ?
             TranslationManager.Translation.HideBottomGallery :
             TranslationManager.Translation.HideBottomGallery :
             TranslationManager.Translation.ShowBottomGallery;
             TranslationManager.Translation.ShowBottomGallery;
         
         
-        translationViewModel.IsLooping = Settings.UIProperties.Looping
+        translationViewModel.IsLooping.Value = Settings.UIProperties.Looping
             ? TranslationManager.Translation.LoopingEnabled
             ? TranslationManager.Translation.LoopingEnabled
             : TranslationManager.Translation.LoopingDisabled;
             : TranslationManager.Translation.LoopingDisabled;
         
         
-        translationViewModel.IsCtrlToZoom = Settings.Zoom.CtrlZoom
+        translationViewModel.IsCtrlToZoom.Value = Settings.Zoom.CtrlZoom
             ? TranslationManager.Translation.CtrlToZoom
             ? TranslationManager.Translation.CtrlToZoom
             : TranslationManager.Translation.ScrollToZoom;
             : TranslationManager.Translation.ScrollToZoom;
         
         
-        translationViewModel.IsShowingBottomToolbar = Settings.UIProperties.ShowBottomNavBar
+        translationViewModel.IsShowingBottomToolbar.Value = Settings.UIProperties.ShowBottomNavBar
             ? TranslationManager.Translation.HideBottomToolbar
             ? TranslationManager.Translation.HideBottomToolbar
             : TranslationManager.Translation.ShowBottomToolbar;
             : TranslationManager.Translation.ShowBottomToolbar;
         
         
-        translationViewModel.IsShowingFadingUIButtons = Settings.UIProperties.ShowAltInterfaceButtons
+        translationViewModel.IsShowingFadingUIButtons.Value = Settings.UIProperties.ShowAltInterfaceButtons
             ? TranslationManager.Translation.DisableFadeInButtonsOnHover
             ? TranslationManager.Translation.DisableFadeInButtonsOnHover
             : TranslationManager.Translation.ShowFadeInButtonsOnHover;
             : TranslationManager.Translation.ShowFadeInButtonsOnHover;
         
         
-        translationViewModel.IsUsingTouchpad = Settings.Zoom.IsUsingTouchPad
+        translationViewModel.IsUsingTouchpad.Value = Settings.Zoom.IsUsingTouchPad
             ? TranslationManager.Translation.UsingTouchpad
             ? TranslationManager.Translation.UsingTouchpad
             : TranslationManager.Translation.UsingMouse;
             : TranslationManager.Translation.UsingMouse;
     }
     }

+ 97 - 81
src/PicView.Avalonia/SettingsManagement/SettingsUpdater.cs

@@ -18,25 +18,29 @@ public static class SettingsUpdater
 {
 {
     public static void ValidateGallerySettings(MainViewModel vm, bool settingsExists)
     public static void ValidateGallerySettings(MainViewModel vm, bool settingsExists)
     {
     {
-        vm.GetFullGalleryItemHeight = Settings.Gallery.ExpandedGalleryItemSize;
-        vm.GetBottomGalleryItemHeight = Settings.Gallery.BottomGalleryItemSize;
+        if (vm.Gallery is not {} gallery)
+        {
+            return;
+        }
+        vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.Value  = Settings.Gallery.ExpandedGalleryItemSize;
+        vm.Gallery.GalleryItem.BottomGalleryItemHeight.Value = Settings.Gallery.BottomGalleryItemSize;
         if (!settingsExists)
         if (!settingsExists)
         {
         {
-            vm.GetBottomGalleryItemHeight = GalleryDefaults.DefaultBottomGalleryHeight;
-            vm.GetFullGalleryItemHeight = GalleryDefaults.DefaultFullGalleryHeight;
+            vm.Gallery.GalleryItem.BottomGalleryItemHeight.Value = GalleryDefaults.DefaultBottomGalleryHeight;
+            vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.Value = GalleryDefaults.DefaultFullGalleryHeight;
         }
         }
 
 
         // Set default gallery sizes if they are out of range or upgrading from an old version
         // Set default gallery sizes if they are out of range or upgrading from an old version
-        if (vm.GetBottomGalleryItemHeight < vm.MinBottomGalleryItemHeight ||
-            vm.GetBottomGalleryItemHeight > vm.MaxBottomGalleryItemHeight)
+        if (vm.Gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue < GalleryDefaults.MinBottomGalleryItemHeight ||
+            vm.Gallery.GalleryItem.BottomGalleryItemHeight.CurrentValue > GalleryDefaults.MaxBottomGalleryItemHeight)
         {
         {
-            vm.GetBottomGalleryItemHeight = GalleryDefaults.DefaultBottomGalleryHeight;
+            vm.Gallery.GalleryItem.BottomGalleryItemHeight.Value = GalleryDefaults.DefaultBottomGalleryHeight;
         }
         }
 
 
-        if (vm.GetFullGalleryItemHeight < vm.MinFullGalleryItemHeight ||
-            vm.GetFullGalleryItemHeight > vm.MaxFullGalleryItemHeight)
+        if (vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.CurrentValue < GalleryDefaults.MinFullGalleryItemHeight ||
+            vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.CurrentValue > GalleryDefaults.MaxFullGalleryItemHeight)
         {
         {
-            vm.GetFullGalleryItemHeight = GalleryDefaults.DefaultFullGalleryHeight;
+            vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.Value = GalleryDefaults.DefaultFullGalleryHeight;
         }
         }
 
 
         if (settingsExists)
         if (settingsExists)
@@ -56,51 +60,33 @@ public static class SettingsUpdater
     }
     }
 
 
     public static void InitializeSettings(MainViewModel vm)
     public static void InitializeSettings(MainViewModel vm)
-    {    
+    {
         // Set corner radius on macOS
         // Set corner radius on macOS
         if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         {
         {
-            vm.BottomCornerRadius = new CornerRadius(0, 0, 8, 8);
+            vm.MainWindow.BottomCornerRadius.Value = new CornerRadius(0, 0, 8, 8);
         }
         }
         
         
-        vm.TitlebarHeight = Settings.WindowProperties.Fullscreen
-            || !Settings.UIProperties.ShowInterface
+        vm.MainWindow.TitlebarHeight.Value = Settings.WindowProperties.Fullscreen
+                                       || !Settings.UIProperties.ShowInterface
             ? 0
             ? 0
             : SizeDefaults.MainTitlebarHeight;
             : SizeDefaults.MainTitlebarHeight;
-        vm.BottombarHeight = Settings.WindowProperties.Fullscreen
-                             || !Settings.UIProperties.ShowInterface
+        vm.MainWindow.BottombarHeight.Value = Settings.WindowProperties.Fullscreen
+                                              || !Settings.UIProperties.ShowInterface
             ? 0
             ? 0
             : SizeDefaults.BottombarHeight;
             : SizeDefaults.BottombarHeight;
-        vm.GetNavSpeed = Settings.UIProperties.NavSpeed;
-        vm.GetSlideshowSpeed = Settings.UIProperties.SlideShowTimer;
-        vm.GetZoomSpeed = Settings.Zoom.ZoomSpeed;
-        vm.PicViewer.IsShowingSideBySide = Settings.ImageScaling.ShowImageSideBySide;
-        vm.IsBottomGalleryShown = Settings.Gallery.IsBottomGalleryShown;
-        vm.IsBottomGalleryShownInHiddenUI = Settings.Gallery.ShowBottomGalleryInHiddenUI;
-        vm.IsAvoidingZoomingOut  = Settings.Zoom.AvoidZoomingOut;
-        vm.IsUIShown  = Settings.UIProperties.ShowInterface;
-        vm.IsTopToolbarShown  = Settings.UIProperties.ShowInterface;
-        vm.IsBottomToolbarShown   = Settings.UIProperties.ShowBottomNavBar &&
+        vm.PicViewer.IsShowingSideBySide.Value = Settings.ImageScaling.ShowImageSideBySide;
+        vm.MainWindow.IsUIShown.Value  = Settings.UIProperties.ShowInterface;
+        vm.MainWindow.IsTopToolbarShown.Value  = Settings.UIProperties.ShowInterface;
+        vm.MainWindow.IsBottomToolbarShown.Value   = Settings.UIProperties.ShowBottomNavBar &&
                                     Settings.UIProperties.ShowInterface;
                                     Settings.UIProperties.ShowInterface;
-        vm.IsShowingTaskbarProgress  = Settings.UIProperties.IsTaskbarProgressEnabled;
-        vm.IsFullscreen  = Settings.WindowProperties.Fullscreen;
-        vm.IsTopMost  = Settings.WindowProperties.TopMost;
-        vm.IsIncludingSubdirectories = Settings.Sorting.IncludeSubDirectories;
-        vm.IsStretched = Settings.ImageScaling.StretchImage;
-        vm.IsLooping  = Settings.UIProperties.Looping;
-        vm.IsAutoFit  = Settings.WindowProperties.AutoFit;
-        vm.IsStayingCentered  = Settings.WindowProperties.KeepCentered;
-        vm.IsOpeningInSameWindow  = Settings.UIProperties.OpenInSameWindow;
-        vm.IsShowingConfirmationOnEsc  = Settings.UIProperties.ShowConfirmationOnEsc;   
-        vm.IsUsingTouchpad  = Settings.Zoom.IsUsingTouchPad;
-        vm.IsAscending  = Settings.Sorting.Ascending;
-        vm.BackgroundChoice = Settings.UIProperties.BgColorChoice;
-        vm.IsConstrainingBackgroundColor = Settings.UIProperties.IsConstrainBackgroundColorEnabled;
+        vm.MainWindow.IsFullscreen.Value  = Settings.WindowProperties.Fullscreen;
+        vm.MainWindow.BackgroundChoice.Value = Settings.UIProperties.BgColorChoice;
     }
     }
     
     
     public static async Task ResetSettings(MainViewModel vm)
     public static async Task ResetSettings(MainViewModel vm)
     {
     {
-        vm.IsLoading = true;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = true;
 
 
         try
         try
         {
         {
@@ -116,10 +102,13 @@ public static class SettingsUpdater
             {
             {
                 TurnOffUsingTouchpad(vm);
                 TurnOffUsingTouchpad(vm);
             }
             }
-        
-            vm.GetBottomGalleryItemHeight = GalleryDefaults.DefaultBottomGalleryHeight;
-            vm.GetFullGalleryItemHeight = GalleryDefaults.DefaultFullGalleryHeight;
-        
+
+            if (vm.Gallery is not null)
+            {
+                vm.Gallery.GalleryItem.BottomGalleryItemHeight.Value = GalleryDefaults.DefaultBottomGalleryHeight;
+                vm.Gallery.GalleryItem.ExpandedGalleryItemHeight.Value = GalleryDefaults.DefaultFullGalleryHeight;
+            }
+            
             if (string.IsNullOrWhiteSpace(Settings.Gallery.BottomGalleryStretchMode))
             if (string.IsNullOrWhiteSpace(Settings.Gallery.BottomGalleryStretchMode))
             {
             {
                 Settings.Gallery.BottomGalleryStretchMode = "UniformToFill";
                 Settings.Gallery.BottomGalleryStretchMode = "UniformToFill";
@@ -153,7 +142,7 @@ public static class SettingsUpdater
         finally
         finally
         {
         {
             TitleManager.SetTitle(vm);
             TitleManager.SetTitle(vm);
-            vm.IsLoading = false;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         }
         }
     }
     }
 
 
@@ -174,15 +163,22 @@ public static class SettingsUpdater
     public static void TurnOffUsingTouchpad(MainViewModel vm)
     public static void TurnOffUsingTouchpad(MainViewModel vm)
     {
     {
         Settings.Zoom.IsUsingTouchPad = false;
         Settings.Zoom.IsUsingTouchPad = false;
-        vm.Translation.IsUsingTouchpad = TranslationManager.Translation.UsingMouse;
-        vm.IsUsingTouchpad = false;
+        vm.Translation.IsUsingTouchpad.Value = TranslationManager.Translation.UsingMouse;
+        if (vm.SettingsViewModel is not null)
+        {
+            vm.SettingsViewModel.IsUsingTouchpad.Value = false;
+        }
+        
     }
     }
     
     
     public static void TurnOnUsingTouchpad(MainViewModel vm)
     public static void TurnOnUsingTouchpad(MainViewModel vm)
     {
     {
         Settings.Zoom.IsUsingTouchPad = true;
         Settings.Zoom.IsUsingTouchPad = true;
-        vm.Translation.IsUsingTouchpad = TranslationManager.Translation.UsingTouchpad;
-        vm.IsUsingTouchpad = true;
+        vm.Translation.IsUsingTouchpad.Value = TranslationManager.Translation.UsingTouchpad;
+        if (vm.SettingsViewModel is not null)
+        {
+            vm.SettingsViewModel.IsUsingTouchpad.Value = true;
+        }
     }
     }
     
     
     public static async Task ToggleSubdirectories(MainViewModel vm)
     public static async Task ToggleSubdirectories(MainViewModel vm)
@@ -197,10 +193,24 @@ public static class SettingsUpdater
         }
         }
         await SaveSettingsAsync();
         await SaveSettingsAsync();
     }
     }
+
+    public static async Task ToggleStretch(MainViewModel vm)
+    {
+        if (Settings.ImageScaling.StretchImage)
+        {
+            Settings.ImageScaling.StretchImage = false;
+        }
+        else
+        {
+            Settings.ImageScaling.StretchImage = true;
+        }
+        await WindowResizing.SetSizeAsync(vm).ConfigureAwait(false);
+        await SaveSettingsAsync();
+    }
     
     
     public static async Task TurnOffSubdirectories(MainViewModel vm)
     public static async Task TurnOffSubdirectories(MainViewModel vm)
     {
     {
-        vm.IsIncludingSubdirectories = false;
+        vm.GlobalSettings.IsIncludingSubdirectories.Value = false;
         Settings.Sorting.IncludeSubDirectories = false;
         Settings.Sorting.IncludeSubDirectories = false;
 
 
         if (!NavigationManager.CanNavigate(vm))
         if (!NavigationManager.CanNavigate(vm))
@@ -214,7 +224,7 @@ public static class SettingsUpdater
     
     
     public static async Task TurnOnSubdirectories(MainViewModel vm)
     public static async Task TurnOnSubdirectories(MainViewModel vm)
     {
     {
-        vm.IsIncludingSubdirectories = true;
+        vm.GlobalSettings.IsIncludingSubdirectories.Value = true;
         Settings.Sorting.IncludeSubDirectories = true;
         Settings.Sorting.IncludeSubDirectories = true;
         
         
         if (!NavigationManager.CanNavigate(vm))
         if (!NavigationManager.CanNavigate(vm))
@@ -257,12 +267,18 @@ public static class SettingsUpdater
         if (Settings.UIProperties.IsConstrainBackgroundColorEnabled)
         if (Settings.UIProperties.IsConstrainBackgroundColorEnabled)
         {
         {
             Settings.UIProperties.IsConstrainBackgroundColorEnabled = false;
             Settings.UIProperties.IsConstrainBackgroundColorEnabled = false;
-            vm.IsConstrainingBackgroundColor = false;
+            if (vm.SettingsViewModel is not null)
+            {
+                vm.SettingsViewModel.IsConstrainingBackgroundColor.Value = false;
+            }
         }
         }
         else
         else
         {
         {
             Settings.UIProperties.IsConstrainBackgroundColorEnabled = true;
             Settings.UIProperties.IsConstrainBackgroundColorEnabled = true;
-            vm.IsConstrainingBackgroundColor = true;
+            if (vm.SettingsViewModel is not null)
+            {
+                vm.SettingsViewModel.IsConstrainingBackgroundColor.Value = true;
+            }
         }
         }
         
         
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
@@ -312,8 +328,8 @@ public static class SettingsUpdater
     public static void TurnOffSideBySide(MainViewModel vm)
     public static void TurnOffSideBySide(MainViewModel vm)
     {
     {
         Settings.ImageScaling.ShowImageSideBySide = false;
         Settings.ImageScaling.ShowImageSideBySide = false;
-        vm.PicViewer.IsShowingSideBySide = false;
-        vm.PicViewer.SecondaryImageSource = null;
+        vm.PicViewer.IsShowingSideBySide.Value = false;
+        vm.PicViewer.SecondaryImageSource.Value = null;
         WindowResizing.SetSize(vm);
         WindowResizing.SetSize(vm);
         TitleManager.SetTitle(vm);
         TitleManager.SetTitle(vm);
     }
     }
@@ -321,7 +337,7 @@ public static class SettingsUpdater
     public static async Task TurnOnSideBySide(MainViewModel vm)
     public static async Task TurnOnSideBySide(MainViewModel vm)
     {
     {
         Settings.ImageScaling.ShowImageSideBySide = true;
         Settings.ImageScaling.ShowImageSideBySide = true;
-        vm.PicViewer.IsShowingSideBySide = true;
+        vm.PicViewer.IsShowingSideBySide.Value = true;
         if (NavigationManager.CanNavigate(vm))
         if (NavigationManager.CanNavigate(vm))
         {
         {
             var preloadValue = await NavigationManager.GetNextPreLoadValueAsync();
             var preloadValue = await NavigationManager.GetNextPreLoadValueAsync();
@@ -332,15 +348,15 @@ public static class SettingsUpdater
 #endif
 #endif
                 return;
                 return;
             }
             }
-            vm.PicViewer.SecondaryImageSource = preloadValue.ImageModel.Image;
+            vm.PicViewer.SecondaryImageSource.Value = preloadValue.ImageModel.Image;
             var imageModel1 = new ImageModel
             var imageModel1 = new ImageModel
             {
             {
-                FileInfo = vm.PicViewer.FileInfo,
-                PixelWidth = (int)vm.PicViewer.ImageWidth,
-                PixelHeight = (int)vm.PicViewer.ImageHeight,
-                ImageType = vm.PicViewer.ImageType,
+                FileInfo = vm.PicViewer.FileInfo.CurrentValue,
+                PixelWidth = (int)vm.PicViewer.ImageWidth.CurrentValue,
+                PixelHeight = (int)vm.PicViewer.ImageHeight.CurrentValue,
+                ImageType = vm.PicViewer.ImageType.CurrentValue,
                 Image = vm.PicViewer.ImageSource,
                 Image = vm.PicViewer.ImageSource,
-                EXIFOrientation = vm.PicViewer.ExifOrientation
+                EXIFOrientation = vm.PicViewer.ExifOrientation.CurrentValue
             };
             };
             var imageModel2 = new ImageModel
             var imageModel2 = new ImageModel
             {
             {
@@ -353,8 +369,8 @@ public static class SettingsUpdater
             };
             };
             await Dispatcher.UIThread.InvokeAsync(() =>
             await Dispatcher.UIThread.InvokeAsync(() =>
             {
             {
-                WindowResizing.SetSize(vm.PicViewer.ImageWidth, vm.PicViewer.ImageHeight, preloadValue.ImageModel.PixelWidth,
-                    preloadValue.ImageModel.PixelHeight, vm.RotationAngle, vm);
+                WindowResizing.SetSize(vm.PicViewer.ImageWidth.CurrentValue, vm.PicViewer.ImageHeight.CurrentValue, preloadValue.ImageModel.PixelWidth,
+                    preloadValue.ImageModel.PixelHeight, vm.GlobalSettings.RotationAngle.CurrentValue, vm);
                 TitleManager.SetSideBySideTitle(vm, imageModel1, imageModel2);
                 TitleManager.SetSideBySideTitle(vm, imageModel1, imageModel2);
             });
             });
         }
         }
@@ -383,20 +399,20 @@ public static class SettingsUpdater
     
     
     public static void TurnOffScroll(MainViewModel vm)
     public static void TurnOffScroll(MainViewModel vm)
     {
     {
-        vm.ToggleScrollBarVisibility = ScrollBarVisibility.Disabled;
-        vm.Translation.IsScrolling = TranslationManager.Translation.ScrollingDisabled;
-        vm.IsScrollingEnabled = false;
+        vm.MainWindow.ToggleScrollBarVisibility.Value = ScrollBarVisibility.Disabled;
+        vm.Translation.IsScrolling.Value = TranslationManager.Translation.ScrollingDisabled;
+        vm.GlobalSettings.IsScrollingEnabled.Value = false;
         Settings.Zoom.ScrollEnabled = false;
         Settings.Zoom.ScrollEnabled = false;
-        vm.RightControlOffSetMargin = new Thickness(0);
+        vm.MainWindow.RightControlOffSetMargin.Value = new Thickness(0);
     }
     }
     
     
     public static void TurnOnScroll(MainViewModel vm)
     public static void TurnOnScroll(MainViewModel vm)
     {
     {
-        vm.ToggleScrollBarVisibility = ScrollBarVisibility.Visible;
-        vm.Translation.IsScrolling = TranslationManager.Translation.ScrollingEnabled;
-        vm.IsScrollingEnabled = true;
+        vm.MainWindow.ToggleScrollBarVisibility.Value = ScrollBarVisibility.Visible;
+        vm.Translation.IsScrolling.Value = TranslationManager.Translation.ScrollingEnabled;
+        vm.GlobalSettings.IsScrollingEnabled.Value = true;
         Settings.Zoom.ScrollEnabled = true;
         Settings.Zoom.ScrollEnabled = true;
-        vm.RightControlOffSetMargin = new Thickness(0,0,30,0);
+        vm.MainWindow.RightControlOffSetMargin.Value = new Thickness(0,0,30,0);
     }
     }
     
     
     public static async Task ToggleCtrlZoom(MainViewModel vm)
     public static async Task ToggleCtrlZoom(MainViewModel vm)
@@ -407,7 +423,7 @@ public static class SettingsUpdater
         }
         }
         
         
         Settings.Zoom.CtrlZoom = !Settings.Zoom.CtrlZoom;
         Settings.Zoom.CtrlZoom = !Settings.Zoom.CtrlZoom;
-        vm.Translation.IsCtrlToZoom = Settings.Zoom.CtrlZoom
+        vm.Translation.IsCtrlToZoom.Value = Settings.Zoom.CtrlZoom
             ? TranslationManager.Translation.CtrlToZoom
             ? TranslationManager.Translation.CtrlToZoom
             : TranslationManager.Translation.ScrollToZoom;
             : TranslationManager.Translation.ScrollToZoom;
         
         
@@ -421,19 +437,19 @@ public static class SettingsUpdater
             return;
             return;
         }
         }
         var isNavigatingWithCtrl = Settings.Zoom.CtrlZoom;
         var isNavigatingWithCtrl = Settings.Zoom.CtrlZoom;
-        vm.ChangeCtrlZoomImage = isNavigatingWithCtrl ? leftRightArrowsImage as DrawingImage : scanEyeImage as DrawingImage;
+        vm.MainWindow.ChangeCtrlZoomImage.Value = isNavigatingWithCtrl ? leftRightArrowsImage as DrawingImage : scanEyeImage as DrawingImage;
         await SaveSettingsAsync().ConfigureAwait(false);
         await SaveSettingsAsync().ConfigureAwait(false);
     }
     }
     
     
     public static void TurnOffCtrlZoom(MainViewModel vm)
     public static void TurnOffCtrlZoom(MainViewModel vm)
     {
     {
         Settings.Zoom.CtrlZoom = false;
         Settings.Zoom.CtrlZoom = false;
-        vm.Translation.IsCtrlToZoom = TranslationManager.Translation.ScrollToZoom;
+        vm.Translation.IsCtrlToZoom.Value = TranslationManager.Translation.ScrollToZoom;
         if (!Application.Current.TryGetResource("ScanEyeImage", Application.Current.RequestedThemeVariant, out var scanEyeImage ))
         if (!Application.Current.TryGetResource("ScanEyeImage", Application.Current.RequestedThemeVariant, out var scanEyeImage ))
         {
         {
             return;
             return;
         }
         }
-        vm.ChangeCtrlZoomImage = scanEyeImage as DrawingImage;
+        vm.MainWindow.ChangeCtrlZoomImage.Value = scanEyeImage as DrawingImage;
     }
     }
     
     
     public static async Task ToggleLooping(MainViewModel vm)
     public static async Task ToggleLooping(MainViewModel vm)
@@ -445,10 +461,10 @@ public static class SettingsUpdater
         
         
         var value = !Settings.UIProperties.Looping;
         var value = !Settings.UIProperties.Looping;
         Settings.UIProperties.Looping = value;
         Settings.UIProperties.Looping = value;
-        vm.Translation.IsLooping = value
+        vm.Translation.IsLooping.Value = value
             ? TranslationManager.Translation.LoopingEnabled
             ? TranslationManager.Translation.LoopingEnabled
             : TranslationManager.Translation.LoopingDisabled;
             : TranslationManager.Translation.LoopingDisabled;
-        vm.IsLooping = value;
+        vm.GlobalSettings.IsLooping.Value = value;
 
 
         var msg = value
         var msg = value
             ? TranslationManager.Translation.LoopingEnabled
             ? TranslationManager.Translation.LoopingEnabled
@@ -461,8 +477,8 @@ public static class SettingsUpdater
     public static void TurnOffLooping(MainViewModel vm)
     public static void TurnOffLooping(MainViewModel vm)
     {
     {
         Settings.UIProperties.Looping = false;
         Settings.UIProperties.Looping = false;
-        vm.Translation.IsLooping = TranslationManager.Translation.LoopingDisabled;
-        vm.IsLooping = false;
+        vm.Translation.IsLooping.Value = TranslationManager.Translation.LoopingDisabled;
+        vm.GlobalSettings.IsLooping.Value = false;
     }
     }
     
     
     #endregion
     #endregion

+ 18 - 18
src/PicView.Avalonia/StartUp/QuickLoad.cs

@@ -35,26 +35,26 @@ public static class QuickLoad
         var fileInfo = new FileInfo(file);
         var fileInfo = new FileInfo(file);
         if (!fileInfo.Exists) // If not file, try to load if URL, base64 or directory
         if (!fileInfo.Exists) // If not file, try to load if URL, base64 or directory
         {
         {
-            vm.IsLoading = true;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = true;
             await NavigationManager.LoadPicFromStringAsync(file, vm).ConfigureAwait(false);
             await NavigationManager.LoadPicFromStringAsync(file, vm).ConfigureAwait(false);
             return;
             return;
         }
         }
 
 
         if (file.IsArchive()) // Handle if file exist and is an archive
         if (file.IsArchive()) // Handle if file exist and is an archive
         {
         {
-            vm.IsLoading = true;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = true;
             await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
             await NavigationManager.LoadPicFromArchiveAsync(file, vm).ConfigureAwait(false);
             return;
             return;
         }
         }
 
 
         var magickImage = new MagickImage();
         var magickImage = new MagickImage();
         magickImage.Ping(fileInfo);
         magickImage.Ping(fileInfo);
-        vm.PicViewer.FileInfo = fileInfo;
+        vm.PicViewer.FileInfo.Value = fileInfo;
         var isLargeImage = magickImage.Width * magickImage.Height > 5000000; // ~5 megapixels threshold
         var isLargeImage = magickImage.Width * magickImage.Height > 5000000; // ~5 megapixels threshold
         if (isLargeImage || Settings.ImageScaling.ShowImageSideBySide)
         if (isLargeImage || Settings.ImageScaling.ShowImageSideBySide)
         {
         {
             // Don't show loading indicator if image is too small
             // Don't show loading indicator if image is too small
-            vm.IsLoading = true;
+            vm.MainWindow.IsLoadingIndicatorShown.Value = true;
         }
         }
 
 
         if (Settings.ImageScaling.ShowImageSideBySide)
         if (Settings.ImageScaling.ShowImageSideBySide)
@@ -66,8 +66,8 @@ public static class QuickLoad
             await SingeImageLoadingAsync(vm, fileInfo, magickImage).ConfigureAwait(false);
             await SingeImageLoadingAsync(vm, fileInfo, magickImage).ConfigureAwait(false);
         }
         }
 
 
-        vm.IsLoading = false;
-        vm.GetIndex = NavigationManager.GetNonZeroIndex;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
+        vm.PicViewer.GetIndex.Value = NavigationManager.GetNonZeroIndex;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -121,7 +121,7 @@ public static class QuickLoad
 
 
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo, magickImage).ConfigureAwait(false);
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo, magickImage).ConfigureAwait(false);
         SetPicViewerValues(vm, imageModel, fileInfo);
         SetPicViewerValues(vm, imageModel, fileInfo);
-        vm.IsLoading = false;
+        vm.MainWindow.IsLoadingIndicatorShown.Value = false;
         if (!Settings.WindowProperties.AutoFit)
         if (!Settings.WindowProperties.AutoFit)
         {
         {
             await Dispatcher.UIThread.InvokeAsync(
             await Dispatcher.UIThread.InvokeAsync(
@@ -144,7 +144,7 @@ public static class QuickLoad
         NavigationManager.InitializeImageIterator(vm);
         NavigationManager.InitializeImageIterator(vm);
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo, magickImage);
         var imageModel = await GetImageModel.GetImageModelAsync(fileInfo, magickImage);
         var secondaryPreloadValue = await NavigationManager.GetNextPreLoadValueAsync();
         var secondaryPreloadValue = await NavigationManager.GetNextPreLoadValueAsync();
-        vm.PicViewer.SecondaryImageSource = secondaryPreloadValue?.ImageModel?.Image;
+        vm.PicViewer.SecondaryImageSource.Value = secondaryPreloadValue?.ImageModel?.Image;
         SetPicViewerValues(vm, imageModel, fileInfo);
         SetPicViewerValues(vm, imageModel, fileInfo);
         await RenderingFixes(vm, imageModel, secondaryPreloadValue.ImageModel);
         await RenderingFixes(vm, imageModel, secondaryPreloadValue.ImageModel);
         TitleManager.SetSideBySideTitle(vm, imageModel, secondaryPreloadValue?.ImageModel);
         TitleManager.SetSideBySideTitle(vm, imageModel, secondaryPreloadValue?.ImageModel);
@@ -169,13 +169,13 @@ public static class QuickLoad
             vm.ImageViewer.MainImage.InitialAnimatedSource = fileInfo.FullName;
             vm.ImageViewer.MainImage.InitialAnimatedSource = fileInfo.FullName;
         }
         }
         
         
-        vm.PicViewer.ImageSource = imageModel.Image;
-        vm.PicViewer.ImageType = imageModel.ImageType;
-        vm.ZoomValue = 1;
-        vm.PicViewer.PixelWidth = imageModel.PixelWidth;
-        vm.PicViewer.PixelHeight = imageModel.PixelHeight;
+        vm.PicViewer.ImageSource.Value = imageModel.Image;
+        vm.PicViewer.ImageType.Value = imageModel.ImageType;
+        vm.GlobalSettings.RotationAngle.Value = 0;
+        vm.PicViewer.PixelWidth.Value = imageModel.PixelWidth;
+        vm.PicViewer.PixelHeight.Value = imageModel.PixelHeight;
 
 
-        vm.PicViewer.ExifOrientation = imageModel.EXIFOrientation;
+        vm.PicViewer.ExifOrientation.Value = imageModel.EXIFOrientation;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -198,7 +198,7 @@ public static class QuickLoad
             if (is1To1)
             if (is1To1)
             {
             {
                 var size = WindowResizing.GetSize(imageModel.PixelWidth, imageModel.PixelHeight,
                 var size = WindowResizing.GetSize(imageModel.PixelWidth, imageModel.PixelHeight,
-                    secondaryModel?.PixelWidth ?? 0, secondaryModel?.PixelHeight ?? 0, vm.RotationAngle, vm);
+                    secondaryModel?.PixelWidth ?? 0, secondaryModel?.PixelHeight ?? 0, vm.GlobalSettings.RotationAngle.CurrentValue, vm);
                 if (!size.HasValue)
                 if (!size.HasValue)
                 {
                 {
                     DebugHelper.LogDebug(nameof(QuickLoadAsync), nameof(RenderingFixes), "Size is null");
                     DebugHelper.LogDebug(nameof(QuickLoadAsync), nameof(RenderingFixes), "Size is null");
@@ -278,7 +278,7 @@ public static class QuickLoad
         if (Settings.Gallery.IsBottomGalleryShown)
         if (Settings.Gallery.IsBottomGalleryShown)
         {
         {
             bool loadGallery;
             bool loadGallery;
-            if (!vm.IsUIShown)
+            if (!vm.MainWindow.IsUIShown.CurrentValue)
             {
             {
                 loadGallery = Settings.Gallery.ShowBottomGalleryInHiddenUI;
                 loadGallery = Settings.Gallery.ShowBottomGalleryInHiddenUI;
             }
             }
@@ -289,7 +289,7 @@ public static class QuickLoad
 
 
             if (loadGallery)
             if (loadGallery)
             {
             {
-                vm.GalleryMode = GalleryMode.BottomNoAnimation;
+                vm.Gallery.GalleryMode.Value = GalleryMode.BottomNoAnimation;
                 tasks.Add(GalleryLoad.LoadGallery(vm, fileInfo.DirectoryName));
                 tasks.Add(GalleryLoad.LoadGallery(vm, fileInfo.DirectoryName));
             }
             }
         }
         }
@@ -300,7 +300,7 @@ public static class QuickLoad
     private static void SetSize(MainViewModel vm, ImageModel imageModel, ImageModel? secondaryModel)
     private static void SetSize(MainViewModel vm, ImageModel imageModel, ImageModel? secondaryModel)
     {
     {
         var size = WindowResizing.GetSize(imageModel.PixelWidth, imageModel.PixelHeight,
         var size = WindowResizing.GetSize(imageModel.PixelWidth, imageModel.PixelHeight,
-            secondaryModel?.PixelWidth ?? 0, secondaryModel?.PixelHeight ?? 0, vm.RotationAngle, vm);
+            secondaryModel?.PixelWidth ?? 0, secondaryModel?.PixelHeight ?? 0, vm.GlobalSettings.RotationAngle.CurrentValue, vm);
         if (!size.HasValue)
         if (!size.HasValue)
         {
         {
             DebugHelper.LogDebug(nameof(QuickLoadAsync), nameof(SetSize), "Size is null");
             DebugHelper.LogDebug(nameof(QuickLoadAsync), nameof(SetSize), "Size is null");

+ 28 - 23
src/PicView.Avalonia/StartUp/StartUpHelper.cs

@@ -17,7 +17,6 @@ using PicView.Avalonia.WindowBehavior;
 using PicView.Core.FileAssociations;
 using PicView.Core.FileAssociations;
 using PicView.Core.FileHistory;
 using PicView.Core.FileHistory;
 using PicView.Core.ProcessHandling;
 using PicView.Core.ProcessHandling;
-using PicView.Core.ViewModels;
 
 
 namespace PicView.Avalonia.StartUp;
 namespace PicView.Avalonia.StartUp;
 
 
@@ -128,8 +127,13 @@ public static class StartUpHelper
         HandleThemeUpdates(vm);
         HandleThemeUpdates(vm);
         
         
         UIHelper.SetControls(desktop);
         UIHelper.SetControls(desktop);
-
-        SettingsUpdater.ValidateGallerySettings(vm, settingsExists);
+        Task.Run(() =>
+        {
+            HandleWindowControlSettings(vm, desktop);
+            SettingsUpdater.ValidateGallerySettings(vm, settingsExists);
+        });
+        
+        vm.MainWindow.LayoutButtonSubscription();
         
         
         // Need to delay setting fullscreen or maximized until after the window is shown to select the correct monitor
         // Need to delay setting fullscreen or maximized until after the window is shown to select the correct monitor
         if (Settings.WindowProperties.Maximized && !Settings.WindowProperties.Fullscreen)
         if (Settings.WindowProperties.Maximized && !Settings.WindowProperties.Fullscreen)
@@ -147,7 +151,7 @@ public static class StartUpHelper
             }, DispatcherPriority.Normal).Wait();
             }, DispatcherPriority.Normal).Wait();
         }
         }
         
         
-        HandleWindowControlSettings(vm, desktop);
+
         SetWindowEventHandlers(window);
         SetWindowEventHandlers(window);
         MenuManager.AddMenus();
         MenuManager.AddMenus();
         
         
@@ -159,8 +163,6 @@ public static class StartUpHelper
         }
         }
 
 
         Application.Current.Name = "PicView";
         Application.Current.Name = "PicView";
-        
-        vm.AssociationsViewModel ??= new FileAssociationsViewModel();
 
 
         if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
         {
         {
@@ -191,8 +193,8 @@ public static class StartUpHelper
         }
         }
         else
         else
         {
         {
-            vm.ToggleScrollBarVisibility = ScrollBarVisibility.Disabled;
-            vm.IsScrollingEnabled = false;
+            vm.MainWindow.ToggleScrollBarVisibility.Value = ScrollBarVisibility.Disabled;
+            vm.GlobalSettings.IsScrollingEnabled.Value = false;
         }
         }
 
 
         if (Settings.WindowProperties.TopMost)
         if (Settings.WindowProperties.TopMost)
@@ -207,7 +209,7 @@ public static class StartUpHelper
         
         
         if (args.Length > 1)
         if (args.Length > 1)
         {
         {
-            vm.CurrentView = vm.ImageViewer;
+            vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, args[1]));
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, args[1]));
         }
         }
         else StartUpMenuOrLastFile(vm);
         else StartUpMenuOrLastFile(vm);
@@ -219,7 +221,7 @@ public static class StartUpHelper
         
         
         if (arg is not null)
         if (arg is not null)
         {
         {
-            vm.CurrentView = vm.ImageViewer;
+            vm.MainWindow.CurrentView.Value = vm.ImageViewer;
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, arg));
             Task.Run(() => QuickLoad.QuickLoadAsync(vm, arg));
         }
         }
         else StartUpMenuOrLastFile(vm);
         else StartUpMenuOrLastFile(vm);
@@ -235,7 +237,7 @@ public static class StartUpHelper
             }
             }
             else
             else
             {
             {
-                vm.CurrentView = vm.ImageViewer;
+                vm.MainWindow.CurrentView.Value = vm.ImageViewer;
                 Task.Run(() => QuickLoad.QuickLoadAsync(vm, Settings.StartUp.LastFile));
                 Task.Run(() => QuickLoad.QuickLoadAsync(vm, Settings.StartUp.LastFile));
             }
             }
         }
         }
@@ -250,34 +252,37 @@ public static class StartUpHelper
         {
         {
             // Starting it in Dispatcher with post fixes occurrences where the text is not centered or the text is missing
             // Starting it in Dispatcher with post fixes occurrences where the text is not centered or the text is missing
             Dispatcher.UIThread.Post(() => { ErrorHandling.ShowStartUpMenu(vm); });
             Dispatcher.UIThread.Post(() => { ErrorHandling.ShowStartUpMenu(vm); });
-            if (Settings.WindowProperties.AutoFit)
+            Dispatcher.UIThread.Post(() =>
             {
             {
-                WindowFunctions.CenterWindowOnScreen();
-            }
+                if (Settings.WindowProperties.AutoFit)
+                {
+                    WindowFunctions.CenterWindowOnScreen();
+                }
+            }, DispatcherPriority.Background);
         }
         }
     }
     }
 
 
     private static void HandleNormalWindow(MainViewModel vm, Window window)
     private static void HandleNormalWindow(MainViewModel vm, Window window)
     {
     {
-        vm.CanResize = true;
-        vm.IsAutoFit = false;
+        vm.MainWindow.CanResize.Value = true;
+        vm.GlobalSettings.IsAutoFit.Value = false;
         if (Settings.UIProperties.ShowInterface)
         if (Settings.UIProperties.ShowInterface)
         {
         {
-            vm.IsTopToolbarShown = true;
-            vm.IsBottomToolbarShown = Settings.UIProperties.ShowBottomNavBar;
+            vm.MainWindow.IsTopToolbarShown.Value = true;
+            vm.MainWindow.IsBottomToolbarShown.Value = Settings.UIProperties.ShowBottomNavBar;
         }
         }
         WindowFunctions.InitializeWindowSizeAndPosition(window);
         WindowFunctions.InitializeWindowSizeAndPosition(window);
     }
     }
 
 
     private static void HandleAutoFit(MainViewModel vm, Window window)
     private static void HandleAutoFit(MainViewModel vm, Window window)
     {
     {
-        vm.SizeToContent = SizeToContent.WidthAndHeight;
-        vm.CanResize = false;
-        vm.IsAutoFit = true;
+        vm.MainWindow.SizeToContent.Value = SizeToContent.WidthAndHeight;
+        vm.MainWindow.CanResize.Value = false;
+        vm.GlobalSettings.IsAutoFit.Value = true;
         if (Settings.UIProperties.ShowInterface)
         if (Settings.UIProperties.ShowInterface)
         {
         {
-            vm.IsTopToolbarShown = true;
-            vm.IsBottomToolbarShown = Settings.UIProperties.ShowBottomNavBar;
+            vm.MainWindow.IsTopToolbarShown.Value = true;
+            vm.MainWindow.IsBottomToolbarShown.Value = Settings.UIProperties.ShowBottomNavBar;
         }
         }
         window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
         window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
     }
     }

+ 28 - 28
src/PicView.Avalonia/UI/HideInterfaceLogic.cs

@@ -20,43 +20,43 @@ public static class HideInterfaceLogic
     {
     {
         if (Settings.UIProperties.ShowInterface)
         if (Settings.UIProperties.ShowInterface)
         {
         {
-            vm.IsUIShown = false;
+            vm.MainWindow.IsUIShown.Value = false;
             Settings.UIProperties.ShowInterface = false;
             Settings.UIProperties.ShowInterface = false;
-            vm.IsTopToolbarShown = false;
-            vm.IsBottomToolbarShown = false;
-            vm.Translation.IsShowingUI = TranslationManager.Translation.ShowUI;
+            vm.MainWindow.IsTopToolbarShown.Value = false;
+            vm.MainWindow.IsBottomToolbarShown.Value = false;
+            vm.Translation.IsShowingUI.Value = TranslationManager.Translation.ShowUI;
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
                 if (!Settings.Gallery.ShowBottomGalleryInHiddenUI)
                 if (!Settings.Gallery.ShowBottomGalleryInHiddenUI)
                 {
                 {
-                    vm.GalleryMode = GalleryMode.Closed;
+                    vm.Gallery.GalleryMode.Value = GalleryMode.Closed;
                     await Dispatcher.UIThread.InvokeAsync(() =>
                     await Dispatcher.UIThread.InvokeAsync(() =>
                     {
                     {
                         if (UIHelper.GetGalleryView.Bounds.Height > 0)
                         if (UIHelper.GetGalleryView.Bounds.Height > 0)
                         {
                         {
-                            vm.GalleryMode = GalleryMode.BottomToClosed;
+                            vm.Gallery.GalleryMode.Value = GalleryMode.BottomToClosed;
                         }
                         }
                     });
                     });
-                    vm.IsBottomGalleryShown = false;
+                    vm.Gallery.IsBottomGalleryShown.Value = false;
                 }
                 }
                 else
                 else
                 {
                 {
-                    vm.IsBottomGalleryShown = Settings.Gallery.ShowBottomGalleryInHiddenUI;
+                    vm.Gallery.IsBottomGalleryShown.Value = Settings.Gallery.ShowBottomGalleryInHiddenUI;
                 }
                 }
             }
             }
         }
         }
         else
         else
         {
         {
-            vm.IsUIShown = true;
-            vm.IsTopToolbarShown = true;
-            vm.Translation.IsShowingUI = TranslationManager.Translation.HideUI;
+            vm.MainWindow.IsUIShown.Value = true;
+            vm.MainWindow.IsTopToolbarShown.Value = true;
+            vm.Translation.IsShowingUI.Value = TranslationManager.Translation.HideUI;
             if (Settings.UIProperties.ShowBottomNavBar)
             if (Settings.UIProperties.ShowBottomNavBar)
             {
             {
-                vm.IsBottomToolbarShown = true;
-                vm.BottombarHeight = SizeDefaults.BottombarHeight;
+                vm.MainWindow.IsBottomToolbarShown.Value = true;
+                vm.MainWindow.BottombarHeight.Value = SizeDefaults.BottombarHeight;
             }
             }
             Settings.UIProperties.ShowInterface = true;
             Settings.UIProperties.ShowInterface = true;
-            vm.TitlebarHeight = SizeDefaults.MainTitlebarHeight;
+            vm.MainWindow.TitlebarHeight.Value = SizeDefaults.MainTitlebarHeight;
             if (!GalleryFunctions.IsFullGalleryOpen)
             if (!GalleryFunctions.IsFullGalleryOpen)
             {
             {
                 if (Settings.Gallery.IsBottomGalleryShown)
                 if (Settings.Gallery.IsBottomGalleryShown)
@@ -67,18 +67,18 @@ public static class HideInterfaceLogic
                         {
                         {
                             if (UIHelper.GetGalleryView.Bounds.Height <= 0)
                             if (UIHelper.GetGalleryView.Bounds.Height <= 0)
                             {
                             {
-                                vm.GalleryMode = GalleryMode.Closed;
+                                vm.Gallery.GalleryMode.Value = GalleryMode.Closed;
                                 GalleryFunctions.OpenBottomGallery(vm);
                                 GalleryFunctions.OpenBottomGallery(vm);
                             }
                             }
                         });
                         });
-                        _ = GalleryLoad.LoadGallery(vm, vm.PicViewer.FileInfo.DirectoryName);
+                        _ = GalleryLoad.LoadGallery(vm, vm.PicViewer.FileInfo.CurrentValue.DirectoryName);
                     }
                     }
 
 
-                    vm.IsBottomGalleryShown = true;
+                    vm.Gallery.IsBottomGalleryShown.Value = true;
                 }
                 }
                 else
                 else
                 {
                 {
-                    vm.IsBottomGalleryShown = false;
+                    vm.Gallery.IsBottomGalleryShown.Value = false;
                 }
                 }
             }
             }
         }
         }
@@ -97,16 +97,16 @@ public static class HideInterfaceLogic
     {
     {
         if (Settings.UIProperties.ShowBottomNavBar)
         if (Settings.UIProperties.ShowBottomNavBar)
         {
         {
-            vm.IsBottomToolbarShown = false;
+            vm.MainWindow.IsBottomToolbarShown.Value = false;
             Settings.UIProperties.ShowBottomNavBar = false;
             Settings.UIProperties.ShowBottomNavBar = false;
-            vm.Translation.IsShowingBottomToolbar = TranslationManager.Translation.ShowBottomToolbar;
+            vm.Translation.IsShowingBottomToolbar.Value = TranslationManager.Translation.ShowBottomToolbar;
         }
         }
         else
         else
         {
         {
-            vm.IsBottomToolbarShown = true;
+            vm.MainWindow.IsBottomToolbarShown.Value = true;
             Settings.UIProperties.ShowBottomNavBar = true;
             Settings.UIProperties.ShowBottomNavBar = true;
-            vm.BottombarHeight = SizeDefaults.BottombarHeight;
-            vm.Translation.IsShowingBottomToolbar = TranslationManager.Translation.HideBottomToolbar;
+            vm.MainWindow.BottombarHeight.Value = SizeDefaults.BottombarHeight;
+            vm.Translation.IsShowingBottomToolbar.Value = TranslationManager.Translation.HideBottomToolbar;
         }
         }
         await Dispatcher.UIThread.InvokeAsync(() =>
         await Dispatcher.UIThread.InvokeAsync(() =>
         {
         {
@@ -122,18 +122,18 @@ public static class HideInterfaceLogic
     {
     {
         Settings.Gallery.ShowBottomGalleryInHiddenUI = !Settings.Gallery
         Settings.Gallery.ShowBottomGalleryInHiddenUI = !Settings.Gallery
             .ShowBottomGalleryInHiddenUI;
             .ShowBottomGalleryInHiddenUI;
-        vm.IsBottomGalleryShownInHiddenUI = Settings.Gallery.ShowBottomGalleryInHiddenUI;
+        vm.Gallery.IsBottomGalleryShownInHiddenUI.Value = Settings.Gallery.ShowBottomGalleryInHiddenUI;
 
 
-        if (!GalleryFunctions.IsFullGalleryOpen)
+        if (!GalleryFunctions.IsFullGalleryOpen && vm.Gallery is not null)
         {
         {
             if (!Settings.UIProperties.ShowInterface && !Settings.Gallery
             if (!Settings.UIProperties.ShowInterface && !Settings.Gallery
                     .ShowBottomGalleryInHiddenUI)
                     .ShowBottomGalleryInHiddenUI)
             {
             {
-                vm.IsBottomGalleryShown = false;
+                vm.Gallery.IsBottomGalleryShown.Value = false;
             }
             }
             else
             else
             {
             {
-                vm.IsBottomGalleryShown = Settings.Gallery.IsBottomGalleryShown;
+                vm.Gallery.IsBottomGalleryShown.Value = Settings.Gallery.IsBottomGalleryShown;
             }
             }
         }
         }
         
         
@@ -145,7 +145,7 @@ public static class HideInterfaceLogic
         Settings.UIProperties.ShowAltInterfaceButtons = !Settings
         Settings.UIProperties.ShowAltInterfaceButtons = !Settings
             .UIProperties.ShowAltInterfaceButtons;
             .UIProperties.ShowAltInterfaceButtons;
         
         
-        vm.Translation.IsShowingFadingUIButtons = Settings.UIProperties.ShowAltInterfaceButtons
+        vm.Translation.IsShowingFadingUIButtons.Value = Settings.UIProperties.ShowAltInterfaceButtons
             ? TranslationManager.Translation.DisableFadeInButtonsOnHover
             ? TranslationManager.Translation.DisableFadeInButtonsOnHover
             : TranslationManager.Translation.ShowFadeInButtonsOnHover;
             : TranslationManager.Translation.ShowFadeInButtonsOnHover;
         
         

+ 16 - 16
src/PicView.Avalonia/UI/MenuManager.cs

@@ -41,10 +41,10 @@ public static class MenuManager
     /// </summary>
     /// </summary>
     public static void CloseMenus(MainViewModel vm)
     public static void CloseMenus(MainViewModel vm)
     {
     {
-        vm.IsFileMenuVisible = false;
-        vm.IsImageMenuVisible = false;
-        vm.IsSettingsMenuVisible = false;
-        vm.IsToolsMenuVisible = false;
+        vm.MainWindow.IsFileMenuVisible.Value = false;
+        vm.MainWindow.IsImageMenuVisible.Value = false;
+        vm.MainWindow.IsSettingsMenuVisible.Value = false;
+        vm.MainWindow.IsToolsMenuVisible.Value = false;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -52,10 +52,10 @@ public static class MenuManager
     /// </summary>
     /// </summary>
     public static bool IsAnyMenuOpen(MainViewModel vm)
     public static bool IsAnyMenuOpen(MainViewModel vm)
     {
     {
-        return vm.IsFileMenuVisible ||
-               vm.IsImageMenuVisible ||
-               vm.IsSettingsMenuVisible ||
-               vm.IsToolsMenuVisible;
+        return vm.MainWindow.IsFileMenuVisible.CurrentValue ||
+               vm.MainWindow.IsImageMenuVisible.CurrentValue ||
+               vm.MainWindow.IsSettingsMenuVisible.CurrentValue ||
+               vm.MainWindow.IsToolsMenuVisible.CurrentValue;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -103,10 +103,10 @@ public static class MenuManager
     {
     {
         return menuType switch
         return menuType switch
         {
         {
-            MenuType.File => vm.IsFileMenuVisible,
-            MenuType.Image => vm.IsImageMenuVisible,
-            MenuType.Settings => vm.IsSettingsMenuVisible,
-            MenuType.Tools => vm.IsToolsMenuVisible,
+            MenuType.File => vm.MainWindow.IsFileMenuVisible.CurrentValue,
+            MenuType.Image => vm.MainWindow.IsImageMenuVisible.CurrentValue,
+            MenuType.Settings => vm.MainWindow.IsSettingsMenuVisible.CurrentValue,
+            MenuType.Tools => vm.MainWindow.IsToolsMenuVisible.CurrentValue,
             _ => false
             _ => false
         };
         };
     }
     }
@@ -116,16 +116,16 @@ public static class MenuManager
         switch (menuType)
         switch (menuType)
         {
         {
             case MenuType.File:
             case MenuType.File:
-                vm.IsFileMenuVisible = state;
+                vm.MainWindow.IsFileMenuVisible.Value = state;
                 break;
                 break;
             case MenuType.Image:
             case MenuType.Image:
-                vm.IsImageMenuVisible = state;
+                vm.MainWindow.IsImageMenuVisible.Value = state;
                 break;
                 break;
             case MenuType.Settings:
             case MenuType.Settings:
-                vm.IsSettingsMenuVisible = state;
+                vm.MainWindow.IsSettingsMenuVisible.Value = state;
                 break;
                 break;
             case MenuType.Tools:
             case MenuType.Tools:
-                vm.IsToolsMenuVisible = state;
+                vm.MainWindow.IsToolsMenuVisible.Value = state;
                 break;
                 break;
         }
         }
     }
     }

+ 45 - 42
src/PicView.Avalonia/UI/TitleManager.cs

@@ -19,10 +19,14 @@ public static class TitleManager
     /// <remarks>Can be used to refresh the title when files are added or removed.</remarks>
     /// <remarks>Can be used to refresh the title when files are added or removed.</remarks>
     public static void SetTitle(MainViewModel vm)
     public static void SetTitle(MainViewModel vm)
     {
     {
+        var pWidth = vm.PicViewer.PixelWidth.Value;
+        var pHeight = vm.PicViewer.PixelHeight.Value;
+        var fileInfo = vm.PicViewer.FileInfo.Value;
+        
         if (!NavigationManager.CanNavigate(vm))
         if (!NavigationManager.CanNavigate(vm))
         {
         {
             string title;
             string title;
-            var s = vm.PicViewer.WindowTitle;
+            var s = vm.PicViewer.WindowTitle.Value;
             var url = s.GetURL();
             var url = s.GetURL();
             if (!string.IsNullOrWhiteSpace(url))
             if (!string.IsNullOrWhiteSpace(url))
             {
             {
@@ -38,17 +42,17 @@ public static class TitleManager
             }
             }
 
 
             var singleImageWindowTitles =
             var singleImageWindowTitles =
-                ImageTitleFormatter.GenerateTitleForSingleImage(vm.PicViewer.PixelWidth, vm.PicViewer.PixelWidth, title, vm.ZoomValue);
-            vm.PicViewer.WindowTitle = singleImageWindowTitles.BaseTitle;
-            vm.PicViewer.Title = singleImageWindowTitles.TitleWithAppName;
-            vm.PicViewer.TitleTooltip = singleImageWindowTitles.TitleWithAppName;
+                ImageTitleFormatter.GenerateTitleForSingleImage(pWidth, pHeight, title, vm.GlobalSettings.RotationAngle.CurrentValue);
+            vm.PicViewer.WindowTitle.Value = singleImageWindowTitles.BaseTitle;
+            vm.PicViewer.Title.Value = singleImageWindowTitles.TitleWithAppName;
+            vm.PicViewer.TitleTooltip.Value = singleImageWindowTitles.TitleWithAppName;
             return;
             return;
         }
         }
 
 
         if (NavigationManager.TiffNavigationInfo is not null)
         if (NavigationManager.TiffNavigationInfo is not null)
         {
         {
-            SetTiffTitle(NavigationManager.TiffNavigationInfo, vm.PicViewer.PixelWidth, vm.PicViewer.PixelHeight,
-                NavigationManager.GetCurrentIndex, vm.PicViewer.FileInfo, vm);
+            SetTiffTitle(NavigationManager.TiffNavigationInfo, pWidth, pHeight,
+                NavigationManager.GetCurrentIndex, vm.PicViewer.FileInfo.Value, vm);
             return;
             return;
         }
         }
 
 
@@ -56,9 +60,9 @@ public static class TitleManager
         {
         {
             var imageModel1 = new ImageModel
             var imageModel1 = new ImageModel
             {
             {
-                FileInfo = vm.PicViewer.FileInfo,
-                PixelWidth = vm.PicViewer.PixelWidth,
-                PixelHeight = vm.PicViewer.PixelHeight
+                FileInfo = vm.PicViewer.FileInfo.Value,
+                PixelWidth = vm.PicViewer.PixelWidth.Value,
+                PixelHeight = vm.PicViewer.PixelHeight.Value
             };
             };
             var nextFileName = NavigationManager.GetNextFileName;
             var nextFileName = NavigationManager.GetNextFileName;
             using var magickImage = new MagickImage();
             using var magickImage = new MagickImage();
@@ -73,12 +77,12 @@ public static class TitleManager
             return;
             return;
         }
         }
 
 
-        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(vm.PicViewer.PixelWidth, vm.PicViewer.PixelHeight,
+        var windowTitles = ImageTitleFormatter.GenerateTitleStrings(pWidth, pHeight,
             NavigationManager.GetCurrentIndex,
             NavigationManager.GetCurrentIndex,
-            vm.PicViewer.FileInfo, vm.ZoomValue, NavigationManager.GetCollection);
-        vm.PicViewer.WindowTitle = windowTitles.TitleWithAppName;
-        vm.PicViewer.Title = windowTitles.BaseTitle;
-        vm.PicViewer.TitleTooltip = windowTitles.FilePathTitle;
+            fileInfo, vm.GlobalSettings.RotationAngle.CurrentValue, NavigationManager.GetCollection);
+        vm.PicViewer.WindowTitle.Value = windowTitles.TitleWithAppName;
+        vm.PicViewer.Title.Value = windowTitles.BaseTitle;
+        vm.PicViewer.TitleTooltip.Value = windowTitles.FilePathTitle;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -88,9 +92,8 @@ public static class TitleManager
     /// <param name="vm">The main view model instance.</param>
     /// <param name="vm">The main view model instance.</param>
     public static void SetLoadingTitle(MainViewModel vm)
     public static void SetLoadingTitle(MainViewModel vm)
     {
     {
-        vm.PicViewer.WindowTitle = $"{TranslationManager.Translation.Loading} - PicView";
-        vm.PicViewer.Title = TranslationManager.Translation.Loading;
-        vm.PicViewer.TitleTooltip = vm.PicViewer.Title;
+        vm.PicViewer.TitleTooltip.Value = vm.PicViewer.WindowTitle.Value = $"{TranslationManager.Translation.Loading} - PicView";
+        vm.PicViewer.Title.Value = TranslationManager.Translation.Loading;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -110,18 +113,18 @@ public static class TitleManager
             }
             }
             imageModel = new ImageModel
             imageModel = new ImageModel
             {
             {
-                FileInfo = vm.PicViewer.FileInfo,
-                PixelWidth = vm.PicViewer.PixelWidth,
-                PixelHeight = vm.PicViewer.PixelHeight
+                FileInfo = vm.PicViewer.FileInfo.Value,
+                PixelWidth = vm.PicViewer.PixelWidth.Value,
+                PixelHeight = vm.PicViewer.PixelHeight.Value
             };
             };
         }
         }
 
 
         var windowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel.PixelWidth, imageModel.PixelHeight,
         var windowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel.PixelWidth, imageModel.PixelHeight,
             NavigationManager.GetCurrentIndex,
             NavigationManager.GetCurrentIndex,
-            imageModel.FileInfo, vm.ZoomValue, NavigationManager.GetCollection);
-        vm.PicViewer.WindowTitle = windowTitles.TitleWithAppName;
-        vm.PicViewer.Title = windowTitles.BaseTitle;
-        vm.PicViewer.TitleTooltip = windowTitles.FilePathTitle;
+            imageModel.FileInfo, vm.GlobalSettings.RotationAngle.CurrentValue, NavigationManager.GetCollection);
+        vm.PicViewer.WindowTitle.Value = windowTitles.TitleWithAppName;
+        vm.PicViewer.Title.Value = windowTitles.BaseTitle;
+        vm.PicViewer.TitleTooltip.Value = windowTitles.FilePathTitle;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -143,9 +146,9 @@ public static class TitleManager
     {
     {
         var singeImageWindowTitles = ImageTitleFormatter.GenerateTiffTitleStrings(width, height, index, fileInfo,
         var singeImageWindowTitles = ImageTitleFormatter.GenerateTiffTitleStrings(width, height, index, fileInfo,
             tiffNavigationInfo, 1, NavigationManager.GetCollection);
             tiffNavigationInfo, 1, NavigationManager.GetCollection);
-        vm.PicViewer.WindowTitle = singeImageWindowTitles.TitleWithAppName;
-        vm.PicViewer.Title = singeImageWindowTitles.BaseTitle;
-        vm.PicViewer.TitleTooltip = singeImageWindowTitles.BaseTitle;
+        vm.PicViewer.WindowTitle.Value = singeImageWindowTitles.TitleWithAppName;
+        vm.PicViewer.Title.Value = singeImageWindowTitles.BaseTitle;
+        vm.PicViewer.TitleTooltip.Value = singeImageWindowTitles.BaseTitle;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -207,9 +210,9 @@ public static class TitleManager
             }
             }
             imageModel1 = new ImageModel
             imageModel1 = new ImageModel
             {
             {
-                FileInfo = vm.PicViewer.FileInfo,
-                PixelWidth = vm.PicViewer.PixelWidth,
-                PixelHeight = vm.PicViewer.PixelHeight
+                FileInfo = vm.PicViewer.FileInfo.Value,
+                PixelWidth = vm.PicViewer.PixelWidth.Value,
+                PixelHeight = vm.PicViewer.PixelHeight.Value
             };
             };
         }
         }
         if (!ValidateImageModel(imageModel2, vm))
         if (!ValidateImageModel(imageModel2, vm))
@@ -235,16 +238,16 @@ public static class TitleManager
         
         
         var firstWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel1.PixelWidth,
         var firstWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel1.PixelWidth,
             imageModel1.PixelHeight, NavigationManager.GetCurrentIndex,
             imageModel1.PixelHeight, NavigationManager.GetCurrentIndex,
-            imageModel1.FileInfo, vm.ZoomValue, NavigationManager.GetCollection);
+            imageModel1.FileInfo, vm.GlobalSettings.RotationAngle.CurrentValue, NavigationManager.GetCollection);
         var secondWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel2.PixelWidth,
         var secondWindowTitles = ImageTitleFormatter.GenerateTitleStrings(imageModel2.PixelWidth,
             imageModel2.PixelHeight, NavigationManager.GetNextIndex,
             imageModel2.PixelHeight, NavigationManager.GetNextIndex,
-            imageModel2.FileInfo, vm.ZoomValue, NavigationManager.GetCollection);
+            imageModel2.FileInfo, vm.GlobalSettings.RotationAngle.CurrentValue, NavigationManager.GetCollection);
         var windowTitle = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd {secondWindowTitles.BaseTitle} - PicView";
         var windowTitle = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd {secondWindowTitles.BaseTitle} - PicView";
         var title = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd  {secondWindowTitles.BaseTitle}";
         var title = $"{firstWindowTitles.BaseTitle} \u21dc || \u21dd  {secondWindowTitles.BaseTitle}";
         var titleTooltip = $"{firstWindowTitles.FilePathTitle} \u21dc || \u21dd  {secondWindowTitles.FilePathTitle}";
         var titleTooltip = $"{firstWindowTitles.FilePathTitle} \u21dc || \u21dd  {secondWindowTitles.FilePathTitle}";
-        vm.PicViewer.WindowTitle = windowTitle;
-        vm.PicViewer.Title = title;
-        vm.PicViewer.TitleTooltip = titleTooltip;
+        vm.PicViewer.WindowTitle.Value = windowTitle;
+        vm.PicViewer.Title.Value = title;
+        vm.PicViewer.TitleTooltip.Value = titleTooltip;
     }
     }
 
 
     /// <summary>
     /// <summary>
@@ -257,16 +260,16 @@ public static class TitleManager
     /// </remarks>
     /// </remarks>
     public static void SetNoImageTitle(MainViewModel vm)
     public static void SetNoImageTitle(MainViewModel vm)
     {
     {
-        vm.PicViewer.Title = TranslationManager.Translation.NoImage;
-        vm.PicViewer.WindowTitle = TranslationManager.Translation.NoImage + " - PicView";
-        vm.PicViewer.TitleTooltip = TranslationManager.Translation.NoImage;
+        vm.PicViewer.Title.Value = TranslationManager.Translation.NoImage;
+        vm.PicViewer.WindowTitle.Value = TranslationManager.Translation.NoImage + " - PicView";
+        vm.PicViewer.TitleTooltip.Value = TranslationManager.Translation.NoImage;
     }
     }
 
 
     private static void ReturnError(MainViewModel vm)
     private static void ReturnError(MainViewModel vm)
     {
     {
-        vm.PicViewer.WindowTitle =
-            vm.PicViewer.Title =
-                vm.PicViewer.TitleTooltip = TranslationManager.GetTranslation("UnableToRender");
+        vm.PicViewer.WindowTitle.Value =
+            vm.PicViewer.Title.Value =
+                vm.PicViewer.TitleTooltip.Value = TranslationManager.GetTranslation("UnableToRender");
     }
     }
 
 
     private static bool ValidateImageModel(ImageModel? imageModel, MainViewModel vm)
     private static bool ValidateImageModel(ImageModel? imageModel, MainViewModel vm)

+ 16 - 15
src/PicView.Avalonia/UI/UIHelper.cs

@@ -10,6 +10,7 @@ using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.Views;
 using PicView.Avalonia.Views;
 using PicView.Avalonia.Views.UC;
 using PicView.Avalonia.Views.UC;
 using PicView.Avalonia.WindowBehavior;
 using PicView.Avalonia.WindowBehavior;
+using R3.Avalonia;
 
 
 namespace PicView.Avalonia.UI;
 namespace PicView.Avalonia.UI;
 
 
@@ -26,6 +27,9 @@ public static class UIHelper
     public static GalleryAnimationControlView? GetGalleryView { get; private set; }
     public static GalleryAnimationControlView? GetGalleryView { get; private set; }
     public static BottomBar? GetBottomBar { get; private set; }
     public static BottomBar? GetBottomBar { get; private set; }
     public static ToolTipMessage? GetToolTipMessage { get; private set; }
     public static ToolTipMessage? GetToolTipMessage { get; private set; }
+    
+    public static AvaloniaRenderingFrameProvider ? GetFrameProvider { get; private set; }
+    public static void SetFrameProvider(AvaloniaRenderingFrameProvider frameProvider) => GetFrameProvider = frameProvider;
 
 
     /// <summary>
     /// <summary>
     /// Sets up control references from the main desktop application
     /// Sets up control references from the main desktop application
@@ -112,8 +116,8 @@ public static class UIHelper
     /// <param name="next">True to move the cursor to the next button, false for the previous button.</param>
     /// <param name="next">True to move the cursor to the next button, false for the previous button.</param>
     /// <param name="arrow">True to move the cursor on the arrow, false to move the cursor on the button.</param>
     /// <param name="arrow">True to move the cursor on the arrow, false to move the cursor on the button.</param>
     /// <param name="vm">The main view model instance.</param>
     /// <param name="vm">The main view model instance.</param>
-    public static async Task MoveCursorOnButtonClick(bool next, bool arrow, MainViewModel vm) =>
-        await Dispatcher.UIThread.InvokeAsync(() =>
+    public static void MoveCursorOnButtonClick(bool next, bool arrow, MainViewModel vm) =>
+        Dispatcher.UIThread.Post(() =>
         {
         {
             var buttonName = arrow
             var buttonName = arrow
                 ? next ? "ClickArrowRight" : "ClickArrowLeft"
                 ? next ? "ClickArrowRight" : "ClickArrowLeft"
@@ -128,7 +132,7 @@ public static class UIHelper
                 : new Point(50, 10);
                 : new Point(50, 10);
             var p = control.PointToScreen(point);
             var p = control.PointToScreen(point);
             vm.PlatformService?.SetCursorPos(p.X, p.Y);
             vm.PlatformService?.SetCursorPos(p.X, p.Y);
-        });
+        }, DispatcherPriority.ContextIdle);
 
 
     #endregion
     #endregion
 
 
@@ -137,26 +141,23 @@ public static class UIHelper
     /// <summary>
     /// <summary>
     /// Navigates to the next image using the bottom navigation button
     /// Navigates to the next image using the bottom navigation button
     /// </summary>
     /// </summary>
-    public static async Task NextButtonNavigation(MainViewModel vm) =>
-        await SetButtonIntervalAndNavigate(GetBottomBar?.NextButton, true, false, vm);
+    public static async Task NextButtonNavigation() =>
+        await SetButtonIntervalAndNavigate(GetBottomBar?.NextButton, true, false, GetMainView.DataContext as MainViewModel);
 
 
     /// <summary>
     /// <summary>
     /// Navigates to the previous image using the bottom navigation button
     /// Navigates to the previous image using the bottom navigation button
     /// </summary>
     /// </summary>
-    public static async Task PreviousButtonNavigation(MainViewModel vm) =>
-        await SetButtonIntervalAndNavigate(GetBottomBar?.PreviousButton, false, false, vm);
+    public static async Task PreviousButtonNavigation() =>
+        await SetButtonIntervalAndNavigate(GetBottomBar?.PreviousButton, false, false, GetMainView.DataContext as MainViewModel);
 
 
     /// <summary>
     /// <summary>
     /// Navigates to the next image using the arrow button
     /// Navigates to the next image using the arrow button
     /// </summary>
     /// </summary>
-    public static async Task NextArrowButtonNavigation(MainViewModel vm) =>
-        await SetButtonIntervalAndNavigate(GetMainView?.ClickArrowRight?.PolyButton, true, true, vm);
-
-    /// <summary>
-    /// Navigates to the previous image using the arrow button
-    /// </summary>
-    public static async Task PreviousArrowButtonNavigation(MainViewModel vm) =>
-        await SetButtonIntervalAndNavigate(GetMainView?.ClickArrowLeft?.PolyButton, false, true, vm);
+    public static async Task NextArrowButtonNavigation() =>
+        await SetButtonIntervalAndNavigate(GetMainView?.ClickArrowRight?.PolyButton, true, true, GetMainView.DataContext as MainViewModel);
+    /// <inheritdoc cref="NextArrowButtonNavigation(MainViewModel vm)"/>
+    public static async Task PreviousArrowButtonNavigation() =>
+        await SetButtonIntervalAndNavigate(GetMainView?.ClickArrowLeft?.PolyButton, false, true, GetMainView.DataContext as MainViewModel);
 
 
     private static async Task SetButtonIntervalAndNavigate(RepeatButton? button, bool isNext, bool isArrow,
     private static async Task SetButtonIntervalAndNavigate(RepeatButton? button, bool isNext, bool isArrow,
         MainViewModel vm)
         MainViewModel vm)

+ 57 - 0
src/PicView.Avalonia/ViewModels/FileSortingViewModel.cs

@@ -0,0 +1,57 @@
+using PicView.Avalonia.Functions;
+using PicView.Core.FileSorting;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class FileSortingViewModel : IDisposable
+{
+    public ReactiveCommand SortFilesByNameCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesByName();
+    });
+    
+    public ReactiveCommand SortFilesByCreationTimeCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesByCreationTime();
+    });
+    
+    public ReactiveCommand SortFilesByLastAccessTimeCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesByLastAccessTime();
+    });
+    
+    public ReactiveCommand SortFilesBySizeCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesBySize();
+    });
+    
+    public ReactiveCommand SortFilesByExtensionCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesByExtension();
+    });
+    
+    public ReactiveCommand SortFilesRandomlyCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesRandomly();
+    });
+    
+    public ReactiveCommand SortFilesAscendingCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesAscending();
+    });
+    
+    public ReactiveCommand SortFilesDescendingCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SortFilesDescending();
+    });
+
+    public BindableReactiveProperty<SortFilesBy> SortOrder { get; } = new();
+    
+    public BindableReactiveProperty<bool> IsAscending { get; } = new(Settings.Sorting.Ascending);
+
+    public void Dispose()
+    {
+        Disposable.Dispose();
+    }
+}

+ 36 - 0
src/PicView.Avalonia/ViewModels/GalleryItemViewModel.cs

@@ -0,0 +1,36 @@
+using Avalonia;
+using PicView.Core.Gallery;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class GalleryItemViewModel : IDisposable
+{
+    public void Dispose()
+    {
+        Disposable.Dispose(ItemWidth,
+            ItemHeight,
+            ItemMargin,
+            ExpandedGalleryItemWidth,
+            ExpandedGalleryItemHeight,
+            BottomGalleryItemWidth,
+            BottomGalleryItemHeight);
+    }
+
+    public BindableReactiveProperty<double> ItemWidth { get; } = new(0);
+    public BindableReactiveProperty<double> ItemHeight { get; } = new(0);
+
+    public BindableReactiveProperty<Thickness> ItemMargin { get; } = new();
+
+    public BindableReactiveProperty<double> ExpandedGalleryItemWidth { get; } = new(0);
+    public BindableReactiveProperty<double> ExpandedGalleryItemHeight { get; } = new(0);
+    
+    public BindableReactiveProperty<double> BottomGalleryItemWidth { get; } = new(0);
+    public BindableReactiveProperty<double> BottomGalleryItemHeight { get; } = new(0);
+
+    public double MaxExpandedGalleryItemHeight => GalleryDefaults.MaxFullGalleryItemHeight;
+    public double MinExpandedGalleryItemHeight => GalleryDefaults.MinFullGalleryItemHeight;
+
+    public double MaxGalleryItemHeight => GalleryDefaults.MaxBottomGalleryItemHeight;
+    public double MinGalleryItemHeight => GalleryDefaults.MinBottomGalleryItemHeight;
+}

+ 117 - 0
src/PicView.Avalonia/ViewModels/GalleryViewModel.cs

@@ -0,0 +1,117 @@
+using Avalonia;
+using Avalonia.Layout;
+using Avalonia.Media;
+using PicView.Avalonia.Functions;
+using PicView.Avalonia.Gallery;
+using PicView.Core.Gallery;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class GalleryViewModel : IDisposable
+{
+    public GalleryItemViewModel GalleryItem { get; } = new();
+
+    public BindableReactiveProperty<Thickness> GalleryMargin { get; } = new();
+
+    public BindableReactiveProperty<GalleryMode> GalleryMode { get; } = new(Core.Gallery.GalleryMode.Closed);
+
+    public BindableReactiveProperty<Stretch> GalleryStretch { get; } = new();
+    public BindableReactiveProperty<VerticalAlignment> GalleryVerticalAlignment { get; } = new();
+    public BindableReactiveProperty<Orientation> GalleryOrientation { get; } = new();
+
+    public BindableReactiveProperty<bool> IsGalleryExpanded { get; } = new();
+    
+    public BindableReactiveProperty<bool> IsBottomGalleryShown { get; } = new(Settings.Gallery.IsBottomGalleryShown);
+    public BindableReactiveProperty<bool> IsBottomGalleryShownInHiddenUI { get; } =
+        new(Settings.Gallery.ShowBottomGalleryInHiddenUI);
+    
+    #region Gallery Stretch IsChecked
+
+    public BindableReactiveProperty<bool> IsUniformBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUniformFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUniformMenuChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUniformToFillBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUniformToFillFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUniformToFillMenuChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillMenuChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsNoneBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsNoneFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsNoneMenuChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsSquareBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsSquareFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsSquareMenuChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillSquareBottomChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillSquareFullChecked { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFillSquareMenuChecked { get; } = new();
+
+    #endregion
+
+    #region Commands
+    public ReactiveCommand ToggleGalleryCommand { get; } = new(ToggleGallery);
+    public ReactiveCommand ToggleBottomGalleryCommand { get; } = new(ToggleBottomGallery);
+    public ReactiveCommand CloseGalleryCommand { get; } = new(CloseGallery);
+    public ReactiveCommand<string> GalleryItemStretchCommand { get; } = new(GalleryItemStretch);
+
+    private static void ToggleGallery(Unit unit) => FunctionsMapper.ToggleGallery();
+    private static void ToggleBottomGallery(Unit unit) => FunctionsMapper.OpenCloseBottomGallery();
+    private static void CloseGallery(Unit unit) => FunctionsMapper.CloseGallery();
+    private static void GalleryItemStretch(string value) => GalleryHelper.SetGalleryItemStretch(value);
+    
+    #endregion
+
+    public void Dispose()
+    {
+        Disposable.Dispose(GalleryItem,
+            GalleryMargin,
+            IsBottomGalleryShown,
+            IsBottomGalleryShownInHiddenUI,
+            GalleryMode,
+            GalleryStretch,
+            GalleryVerticalAlignment,
+            GalleryOrientation,
+            IsGalleryExpanded,
+            ToggleGalleryCommand,
+            ToggleBottomGalleryCommand,
+            CloseGalleryCommand,
+            GalleryItemStretchCommand,
+            IsUniformBottomChecked,
+            IsUniformFullChecked,
+            IsUniformMenuChecked,
+            IsUniformToFillBottomChecked,
+            IsUniformToFillFullChecked,
+            IsUniformToFillMenuChecked,
+            IsFillBottomChecked,
+            IsFillFullChecked,
+            IsFillMenuChecked,
+            IsNoneBottomChecked,
+            IsNoneFullChecked,
+            IsNoneMenuChecked,
+            IsSquareBottomChecked,
+            IsSquareFullChecked,
+            IsSquareMenuChecked,
+            IsFillSquareBottomChecked,
+            IsFillSquareFullChecked,
+            IsFillSquareMenuChecked
+            );
+    }
+}

+ 76 - 101
src/PicView.Avalonia/ViewModels/ImageCropperViewModel.cs

@@ -1,5 +1,4 @@
-using System.Reactive;
-using Avalonia;
+using Avalonia;
 using Avalonia.Media.Imaging;
 using Avalonia.Media.Imaging;
 using ImageMagick;
 using ImageMagick;
 using PicView.Avalonia.Animations;
 using PicView.Avalonia.Animations;
@@ -9,103 +8,62 @@ using PicView.Avalonia.ImageHandling;
 using PicView.Avalonia.Navigation;
 using PicView.Avalonia.Navigation;
 using PicView.Avalonia.UI;
 using PicView.Avalonia.UI;
 using PicView.Core.Localization;
 using PicView.Core.Localization;
-using ReactiveUI;
+using R3;
+using Unit = R3.Unit;
 
 
 namespace PicView.Avalonia.ViewModels;
 namespace PicView.Avalonia.ViewModels;
 
 
-public class ImageCropperViewModel : ReactiveObject
+public class ImageCropperViewModel : IDisposable
 {
 {
     public ImageCropperViewModel(Bitmap bitmap)
     public ImageCropperViewModel(Bitmap bitmap)
     {
     {
-        Bitmap = bitmap;
-        InitializeCommands();
+        CropImageCommand = new ReactiveCommand(SaveCroppedImageAsync);
+        CopyCropImageCommand = new ReactiveCommand(CopyCroppedImageAsync);
+        CloseCropCommand = new ReactiveCommand(HandleCloseCrop);
+        Bitmap.Value = bitmap;
     }
     }
 
 
-    private void InitializeCommands()
-    {
-        CropImageCommand = ReactiveCommand.CreateFromTask(SaveCroppedImageAsync);
-        CopyCropImageCommand = ReactiveCommand.CreateFromTask(CopyCroppedImageAsync);
-        CloseCropCommand = ReactiveCommand.Create(HandleCloseCrop);
-    }
-    
-    public ReactiveCommand<Unit, Unit>? CropImageCommand { get; private set; }
-    public ReactiveCommand<Unit, Unit>? CopyCropImageCommand { get; private set; }
-    public ReactiveCommand<Unit, Unit>? CloseCropCommand { get; private set; }
+    public ReactiveCommand CropImageCommand { get; private set; }
+    public ReactiveCommand CopyCropImageCommand { get; private set; }
+    public ReactiveCommand CloseCropCommand { get; private set; }
 
 
-    public Bitmap Bitmap
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
+    public BindableReactiveProperty<Bitmap> Bitmap { get; } = new();
 
 
-    public int SelectionX
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
+    public BindableReactiveProperty<int> SelectionX { get; } = new();
 
 
-    public int SelectionY
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
+    public BindableReactiveProperty<int> SelectionY { get; } = new();
 
 
-    public double SelectionWidth
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            PixelSelectionWidth = Convert.ToUInt32(SelectionWidth / AspectRatio);
-        }
-    }
-    
-    public uint PixelSelectionWidth
-    {
-        get
-        {
-            return Convert.ToUInt32(SelectionWidth / AspectRatio);
-        }
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
+    public BindableReactiveProperty<double> SelectionWidth { get; } = new();
 
 
-    public double SelectionHeight
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            PixelSelectionHeight = Convert.ToUInt32(SelectionHeight / AspectRatio);
-        } 
-    }
+    public BindableReactiveProperty<uint> PixelSelectionWidth { get; } = new();
 
 
-    public uint PixelSelectionHeight
-    {
-        get
-        {
-            return Convert.ToUInt32(SelectionHeight / AspectRatio);
-        }
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-    
-    public double ImageWidth
+    public BindableReactiveProperty<double> SelectionHeight { get; } = new();
+
+    public BindableReactiveProperty<uint> PixelSelectionHeight { get; } = new();
+
+    public BindableReactiveProperty<double> ImageWidth { get; } = new();
+    public BindableReactiveProperty<double> ImageHeight { get; } = new();
+
+    public BindableReactiveProperty<double> AspectRatio { get; } = new();
+
+    public void Dispose()
     {
     {
-        get;
-        init => this.RaiseAndSetIfChanged(ref field, value);
+        Disposable.Dispose();
     }
     }
-    public double ImageHeight
+
+    public void SetSelectionWidth(uint value)
     {
     {
-        get;
-        init => this.RaiseAndSetIfChanged(ref field, value);
+        SelectionWidth.Value = value;
+        PixelSelectionWidth.Value = Convert.ToUInt32(value / AspectRatio.Value);
     }
     }
-    
-    public double AspectRatio
+
+    public void SetSelectionHeight(uint value)
     {
     {
-        get;
-        init => this.RaiseAndSetIfChanged(ref field, value);
+        SelectionHeight.Value = value;
+        PixelSelectionHeight.Value = Convert.ToUInt32(value / AspectRatio.Value);
     }
     }
 
 
-    private static void HandleCloseCrop()
+    private static void HandleCloseCrop(Unit unit)
     {
     {
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
         if (UIHelper.GetMainView.DataContext is MainViewModel vm)
         {
         {
@@ -113,33 +71,44 @@ public class ImageCropperViewModel : ReactiveObject
         }
         }
     }
     }
 
 
-    private async Task SaveCroppedImageAsync()
+    private async ValueTask SaveCroppedImageAsync(Unit unit, CancellationToken cancellationToken)
     {
     {
-        if (UIHelper.GetMainView.DataContext is not MainViewModel vm) return;
+        if (UIHelper.GetMainView.DataContext is not MainViewModel vm)
+        {
+            return;
+        }
 
 
         var (fileName, fileInfo, bitmap) = PrepareCropData(vm);
         var (fileName, fileInfo, bitmap) = PrepareCropData(vm);
-        
+
         var saveFileDialog = await FilePicker.PickFileForSavingAsync(fileName);
         var saveFileDialog = await FilePicker.PickFileForSavingAsync(fileName);
-        if (saveFileDialog == null) return;
+        if (saveFileDialog == null)
+        {
+            return;
+        }
 
 
         await SaveImage(saveFileDialog, fileInfo, bitmap);
         await SaveImage(saveFileDialog, fileInfo, bitmap);
-        
+
         CropFunctions.CloseCropControl(vm);
         CropFunctions.CloseCropControl(vm);
 
 
-        if (vm.PicViewer.FileInfo.FullName == saveFileDialog)
+        if (vm.PicViewer.FileInfo.CurrentValue.FullName == saveFileDialog)
         {
         {
             await ErrorHandling.ReloadAsync(vm);
             await ErrorHandling.ReloadAsync(vm);
         }
         }
     }
     }
-    
-    private async Task CopyCroppedImageAsync()
+
+    public async Task SaveCroppedImageAsync() => await SaveCroppedImageAsync(Unit.Default, CancellationToken.None);
+
+    private async ValueTask CopyCroppedImageAsync(Unit unit, CancellationToken cancellationToken)
     {
     {
-        if (UIHelper.GetMainView.DataContext is not MainViewModel vm) return;
-        if (vm.PicViewer.ImageSource is not Bitmap sourceBitmap) return;
+        if (UIHelper.TryGetMainViewModel(out var vm) ||
+            vm.PicViewer.ImageSource.CurrentValue is not Bitmap sourceBitmap)
+        {
+            return;
+        }
 
 
-        var x = Convert.ToInt32(SelectionX / AspectRatio);
-        var y = Convert.ToInt32(SelectionY / AspectRatio);
-        var rect = new PixelRect(x, y, (int)PixelSelectionWidth, (int)PixelSelectionHeight);
+        var x = Convert.ToInt32(SelectionX.CurrentValue / AspectRatio.CurrentValue);
+        var y = Convert.ToInt32(SelectionY.CurrentValue / AspectRatio.CurrentValue);
+        var rect = new PixelRect(x, y, (int)PixelSelectionWidth.CurrentValue, (int)PixelSelectionHeight.CurrentValue);
 
 
         var croppedBitmap = new CroppedBitmap(sourceBitmap, rect);
         var croppedBitmap = new CroppedBitmap(sourceBitmap, rect);
         var bitmap = BitmapHelper.ConvertCroppedBitmapToBitmap(croppedBitmap);
         var bitmap = BitmapHelper.ConvertCroppedBitmapToBitmap(croppedBitmap);
@@ -150,17 +119,21 @@ public class ImageCropperViewModel : ReactiveObject
         }
         }
     }
     }
 
 
+    public async Task CopyCroppedImageAsync() => await CopyCroppedImageAsync(Unit.Default, CancellationToken.None);
+
     private (string fileName, FileInfo fileInfo, Bitmap? bitmap) PrepareCropData(MainViewModel vm)
     private (string fileName, FileInfo fileInfo, Bitmap? bitmap) PrepareCropData(MainViewModel vm)
-      => NavigationManager.IsCollectionEmpty ? CreateNewCroppedImage() : (vm.PicViewer.FileInfo.FullName, vm.PicViewer.FileInfo, null);
+        => NavigationManager.IsCollectionEmpty
+            ? CreateNewCroppedImage()
+            : (vm.PicViewer.FileInfo.Value.FullName, vm.PicViewer.FileInfo.Value, null);
 
 
     private (string fileName, FileInfo fileInfo, Bitmap bitmap) CreateNewCroppedImage()
     private (string fileName, FileInfo fileInfo, Bitmap bitmap) CreateNewCroppedImage()
     {
     {
         var fileName = $"{TranslationManager.Translation.Crop} {new Random().Next(9999)}.png";
         var fileName = $"{TranslationManager.Translation.Crop} {new Random().Next(9999)}.png";
-        var x = Convert.ToInt32(SelectionX / AspectRatio);
-        var y = Convert.ToInt32(SelectionY / AspectRatio);
-        var width = (int)PixelSelectionWidth;
-        var height = (int)PixelSelectionHeight;
-        var croppedBitmap = new CroppedBitmap(Bitmap, new PixelRect(x, y, width, height));
+        var x = Convert.ToInt32(SelectionX.CurrentValue / AspectRatio.CurrentValue);
+        var y = Convert.ToInt32(SelectionY.CurrentValue / AspectRatio.CurrentValue);
+        var width = (int)PixelSelectionWidth.CurrentValue;
+        var height = (int)PixelSelectionHeight.CurrentValue;
+        var croppedBitmap = new CroppedBitmap(Bitmap.Value, new PixelRect(x, y, width, height));
         var bitmap = BitmapHelper.ConvertCroppedBitmapToBitmap(croppedBitmap);
         var bitmap = BitmapHelper.ConvertCroppedBitmapToBitmap(croppedBitmap);
         return (fileName, new FileInfo(fileName), bitmap);
         return (fileName, new FileInfo(fileName), bitmap);
     }
     }
@@ -179,10 +152,12 @@ public class ImageCropperViewModel : ReactiveObject
     private async Task SaveWithMagickImage(string saveFilePath, FileInfo fileInfo)
     private async Task SaveWithMagickImage(string saveFilePath, FileInfo fileInfo)
     {
     {
         using var image = new MagickImage(fileInfo.FullName);
         using var image = new MagickImage(fileInfo.FullName);
-        var x = Convert.ToInt32(SelectionX / AspectRatio);
-        var y = Convert.ToInt32(SelectionY / AspectRatio);
-        var geometry = new MagickGeometry(x, y, PixelSelectionWidth, PixelSelectionHeight);
-        
+        var x = Convert.ToInt32(SelectionX.CurrentValue / AspectRatio.CurrentValue);
+        var y = Convert.ToInt32(SelectionY.CurrentValue / AspectRatio.CurrentValue);
+        var width = PixelSelectionWidth.CurrentValue;
+        var height = PixelSelectionHeight.CurrentValue;
+        var geometry = new MagickGeometry(x, y, width, height);
+
         image.Crop(geometry);
         image.Crop(geometry);
         await image.WriteAsync(saveFilePath);
         await image.WriteAsync(saveFilePath);
     }
     }

+ 10 - 1142
src/PicView.Avalonia/ViewModels/MainViewModel.cs

@@ -1,43 +1,27 @@
-using System.Reactive;
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Controls.Primitives;
-using Avalonia.Layout;
-using Avalonia.Media;
-using PicView.Avalonia.Clipboard;
-using PicView.Avalonia.Converters;
-using PicView.Avalonia.FileSystem;
-using PicView.Avalonia.Functions;
-using PicView.Avalonia.Gallery;
-using PicView.Avalonia.ImageTransformations.Rotation;
+using PicView.Avalonia.Functions;
 using PicView.Avalonia.Interfaces;
 using PicView.Avalonia.Interfaces;
-using PicView.Avalonia.LockScreen;
-using PicView.Avalonia.Navigation;
-using PicView.Avalonia.UI;
-using PicView.Avalonia.Wallpaper;
-using PicView.Avalonia.WindowBehavior;
-using PicView.Core.FileSorting;
-using PicView.Core.Gallery;
-using PicView.Core.ProcessHandling;
-using PicView.Core.Sizing;
 using PicView.Core.ViewModels;
 using PicView.Core.ViewModels;
-using ReactiveUI;
 using ImageViewer = PicView.Avalonia.Views.ImageViewer;
 using ImageViewer = PicView.Avalonia.Views.ImageViewer;
 
 
 namespace PicView.Avalonia.ViewModels;
 namespace PicView.Avalonia.ViewModels;
 
 
-public class MainViewModel : ReactiveObject
+public class MainViewModel
 {
 {
     public readonly IPlatformSpecificService? PlatformService;
     public readonly IPlatformSpecificService? PlatformService;
     public readonly IPlatformWindowService? PlatformWindowService;
     public readonly IPlatformWindowService? PlatformWindowService;
     
     
     public TranslationViewModel Translation { get; } = new();
     public TranslationViewModel Translation { get; } = new();
+    public MainWindowViewModel MainWindow { get; } = new();
+    public WindowViewModel Window { get; } = new();
+    public GlobalSettingsViewModel GlobalSettings { get; } = new();
     public SettingsViewModel? SettingsViewModel { get; set; }
     public SettingsViewModel? SettingsViewModel { get; set; }
     public ImageCropperViewModel? Crop { get; set; }
     public ImageCropperViewModel? Crop { get; set; }
+    public NavigationViewModel Navigation { get; } = new();
+    public FileSortingViewModel Sorting { get; } = new();
     public PicViewerModel PicViewer { get; } = new();
     public PicViewerModel PicViewer { get; } = new();
-    
-    public ExifViewModel Exif { get; } = new();
-    
+    public GalleryViewModel Gallery { get; } = new();
+    public ToolsViewModel Tools { get; } = new();
+    public ExifViewModel? Exif { get; set;  }
     public FileAssociationsViewModel? AssociationsViewModel { get; set; }
     public FileAssociationsViewModel? AssociationsViewModel { get; set; }
 
 
     public MainViewModel(IPlatformSpecificService? platformSpecificService, IPlatformWindowService? platformWindowService)
     public MainViewModel(IPlatformSpecificService? platformSpecificService, IPlatformWindowService? platformWindowService)
@@ -45,1134 +29,18 @@ public class MainViewModel : ReactiveObject
         FunctionsMapper.Vm = this;
         FunctionsMapper.Vm = this;
         PlatformService = platformSpecificService;
         PlatformService = platformSpecificService;
         PlatformWindowService = platformWindowService;
         PlatformWindowService = platformWindowService;
-
-        #region Window commands
-
-        ExitCommand = FunctionsHelper.CreateReactiveCommand(WindowFunctions.Close);
-        MinimizeCommand = FunctionsHelper.CreateReactiveCommand(WindowFunctions.Minimize);
-        MaximizeCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Maximize);
-        RestoreCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Restore);
-        ToggleFullscreenCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleFullscreen);
-        NewWindowCommand = FunctionsHelper.CreateReactiveCommand(ProcessHelper.StartNewProcess);
-
-        ShowExifWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowExifWindow);
-        ShowSettingsWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowSettingsWindow);
-        ShowKeybindingsWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowKeybindingsWindow);
-        ShowAboutWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowAboutWindow);
-        ShowBatchResizeWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowBatchResizeWindow);
-        ShowSingleImageResizeWindowCommand =
-            FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowSingleImageResizeWindow);
-        ShowEffectsWindowCommand = FunctionsHelper.CreateReactiveCommand(PlatformWindowService.ShowEffectsWindow);
-
-        #endregion Window commands
-
-        #region Navigation Commands
-
-        NextCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.Next); });
-
-        NextButtonCommand = FunctionsHelper.CreateReactiveCommand(() => { UIHelper.NextButtonNavigation(this); });
-
-        NextArrowButtonCommand = FunctionsHelper.CreateReactiveCommand(() => { UIHelper.NextArrowButtonNavigation(this); });
-
-        NextFolderCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.NextFolder); });
-
-        PreviousCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.Prev); });
-
-        PreviousButtonCommand = FunctionsHelper.CreateReactiveCommand(() => { UIHelper.PreviousButtonNavigation(this); });
-
-        PreviousArrowButtonCommand = FunctionsHelper.CreateReactiveCommand(() => { UIHelper.PreviousArrowButtonNavigation(this); });
-
-        PreviousFolderCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.PrevFolder); });
-
-        Skip10Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Next10);
-
-        Skip100Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Next100);
-
-        Prev10Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Prev10);
-
-        Prev100Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Prev100);
-
-        FirstCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.First);
-
-        LastCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Last);
-
-        ReloadCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Reload);
-
-        #endregion Navigation Commands
-
-        #region Sort Commands
-
-        SortFilesByNameCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesByName);
-
-        SortFilesByCreationTimeCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesByCreationTime);
-
-        SortFilesByLastAccessTimeCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesByLastAccessTime);
-
-        SortFilesBySizeCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesBySize);
-
-        SortFilesByExtensionCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesByExtension);
-
-        SortFilesRandomlyCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesRandomly);
-
-        SortFilesAscendingCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesAscending);
-
-        SortFilesDescendingCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SortFilesDescending);
-
-        #endregion Sort Commands
-
-        #region Menus
-
-        CloseMenuCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.CloseMenus);
-
-        ToggleFileMenuCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleFileMenu);
-
-        ToggleImageMenuCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleImageMenu);
-
-        ToggleSettingsMenuCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleSettingsMenu);
-
-        ToggleToolsMenuCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleToolsMenu);
-
-        #endregion Menus
-
-        #region Image commands
-
-        RotateLeftCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.RotateLeft);
-        RotateLeftButtonCommand = FunctionsHelper.CreateReactiveCommand(async () =>
-        {
-            await RotationNavigation.RotateLeft(this, RotationButton.RotateLeftButton);
-        });
-
-        RotateRightCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.RotateRight);
-        RotateRightButtonCommand = FunctionsHelper.CreateReactiveCommand(async () =>
-        {
-            await RotationNavigation.RotateRight(this, RotationButton.RotateRightButton);
-        });
-        RotateToCommand = FunctionsHelper.CreateReactiveCommand<string>(RotateToTask);
-
-        RotateRightWindowBorderButtonCommand = FunctionsHelper.CreateReactiveCommand(async () =>
-        {
-            await RotationNavigation.RotateRight(this, RotationButton.WindowBorderButton);
-        });
-
-        FlipCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Flip);
-
-        StretchCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Stretch);
-
-        CropCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Crop);
-
-        ToggleScrollCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleScroll);
-
-        OptimizeImageCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.OptimizeImage);
-
-        ChangeBackgroundCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ChangeBackground);
-
-        ShowSideBySideCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SideBySide);
-
-        #endregion Image commands
-
-        #region File commands
-
-        OpenFileCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.Open); });
-
-        OpenLastFileCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.OpenLastFile); });
-
-        SaveFileCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Save);
-
-        SaveFileAsCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SaveAs);
-
-        CopyFileCommand = FunctionsHelper.CreateReactiveCommand<string>(CopyFileTask);
-
-        CopyFilePathCommand = FunctionsHelper.CreateReactiveCommand<string>(CopyFilePathTask);
-
-        FilePropertiesCommand = FunctionsHelper.CreateReactiveCommand<string>(ShowFilePropertiesTask);
-
-        CopyImageCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.CopyImage);
-
-        CopyBase64Command = FunctionsHelper.CreateReactiveCommand<string>(CopyBase64Task);
-
-        CutCommand = FunctionsHelper.CreateReactiveCommand<string>(CutFileTask);
-
-        PasteCommand = FunctionsHelper.CreateReactiveCommand(() => { Task.Run(FunctionsMapper.Paste); });
-
-        OpenWithCommand = FunctionsHelper.CreateReactiveCommand<string>(OpenWithTask);
-
-        RenameCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Rename);
-
-        ResizeCommand = FunctionsHelper.CreateReactiveCommand<int>(ResizeImageByPercentage);
-        ConvertCommand = FunctionsHelper.CreateReactiveCommand<int>(ConvertFileExtension);
-
-        DuplicateFileCommand = FunctionsHelper.CreateReactiveCommand<string>(DuplicateFileTask);
-
-        PrintCommand = FunctionsHelper.CreateReactiveCommand<string>(PrintTask);
-
-        DeleteFileCommand = FunctionsHelper.CreateReactiveCommand<string>(DeleteFileTask);
-
-        RecycleFileCommand = FunctionsHelper.CreateReactiveCommand<string>(RecycleFileTask);
-
-        LocateOnDiskCommand = FunctionsHelper.CreateReactiveCommand<string>(LocateOnDiskTask);
-
-        SetAsWallpaperCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsWallpaperTask);
-        SetAsWallpaperTiledCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsWallpaperTiledTask);
-        SetAsWallpaperStretchedCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsWallpaperStretchedTask);
-        SetAsWallpaperCenteredCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsWallpaperCenteredTask);
-        SetAsWallpaperFilledCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsWallpaperFilledTask);
-
-        SetAsLockScreenCommand = FunctionsHelper.CreateReactiveCommand<string>(SetAsLockScreenTask);
-
-        #endregion File commands
-
-        #region EXIF commands
-
-        SetExifRating0Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set0Star);
-        SetExifRating1Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set1Star);
-        SetExifRating2Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set2Star);
-        SetExifRating3Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set3Star);
-        SetExifRating4Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set4Star);
-        SetExifRating5Command = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Set5Star);
-
-        OpenGoogleLinkCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.OpenGoogleMaps);
-        OpenBingLinkCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.OpenBingMaps);
-
-        #endregion EXIF commands
-
-        #region Gallery Commands
-
-        ToggleGalleryCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleGallery);
-
-        ToggleBottomGalleryCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.OpenCloseBottomGallery);
-
-        CloseGalleryCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.CloseGallery);
-
-        GalleryItemStretchCommand = FunctionsHelper.CreateReactiveCommand<string>(SetGalleryItemStretch);
-
-        #endregion Gallery Commands
-
-        #region UI Commands
-
-        ToggleUICommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleInterface);
-
-        ToggleBottomNavBarCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleBottomToolbar);
-
-        ToggleBottomGalleryShownInHiddenUICommand = FunctionsHelper.CreateReactiveCommand(async () =>
-        {
-            await HideInterfaceLogic.ToggleBottomGalleryShownInHiddenUI(this);
-        });
-
-        ToggleFadeInButtonsOnHoverCommand = FunctionsHelper.CreateReactiveCommand(async () =>
-        {
-            await HideInterfaceLogic.ToggleFadeInButtonsOnHover(this);
-        });
-
-        ChangeCtrlZoomCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ChangeCtrlZoom);
-
-        ColorPickerCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ColorPicker);
-        SlideshowCommand = FunctionsHelper.CreateReactiveCommand<int>(StartSlideShowTask);
-
-        ToggleTaskbarProgressCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleTaskbarProgress);
-        
-        ToggleConstrainBackgroundColorCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleConstrainBackgroundColor);
-
-        #endregion UI Commands
-
-        #region Settings commands
-
-        ChangeAutoFitCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.AutoFitWindow);
-
-        ChangeTopMostCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.SetTopMost);
-
-        ToggleSubdirectoriesCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleSubdirectories);
-
-        ToggleLoopingCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleLooping);
-
-        ResetSettingsCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ResetSettings);
-
-        RestartCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.Restart);
-
-        ToggleUsingTouchpadCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleUsingTouchpad);
-        
-        ToggleOpeningInSameWindowCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ToggleOpeningInSameWindow);
-        
-        ShowSettingsFileCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ShowSettingsFile);
-        
-        ShowKeybindingsFileCommand = FunctionsHelper.CreateReactiveCommand(FunctionsMapper.ShowKeybindingsFile);
-
-        #endregion Settings commands
     }
     }
 
 
     public MainViewModel()
     public MainViewModel()
     {
     {
         // Only use for unit test
         // Only use for unit test
     }
     }
-
-    #region Gallery
-
-    public Thickness GalleryMargin
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsBottomGalleryShown
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsBottomGalleryShownInHiddenUI
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public GalleryMode GalleryMode
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    } = GalleryMode.Closed;
-
-    public Stretch GalleryStretch
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public int SelectedGalleryItemIndex
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public VerticalAlignment GalleryVerticalAlignment
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    } = VerticalAlignment.Bottom;
-
-    public Orientation GalleryOrientation
-    {
-        set => this.RaiseAndSetIfChanged(ref field, value);
-        get;
-    }
-
-    public bool IsFullGalleryOpen
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double GalleryWidth
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double GalleryHeight
-    {
-        get
-        {
-            if (!Settings.Gallery.IsBottomGalleryShown || IsSingleImage || Slideshow.IsRunning)
-            {
-                return 0;
-            }
-
-            if (Settings.WindowProperties.Fullscreen)
-            {
-                return Settings.Gallery.IsBottomGalleryShown
-                    ? GetBottomGalleryItemHeight + (SizeDefaults.ScrollbarSize - 1)
-                    : 0;
-            }
-
-            if (!Settings.Gallery.ShowBottomGalleryInHiddenUI && !IsUIShown)
-            {
-                return 0;
-            }
-
-            return GetBottomGalleryItemHeight + (SizeDefaults.ScrollbarSize - 1);
-        }
-    }
-
-    public double GetGalleryItemWidth
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    } = double.NaN;
-
-    public double GetGalleryItemHeight
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double GetFullGalleryItemHeight
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double GetBottomGalleryItemHeight
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double MaxFullGalleryItemHeight
-    {
-        get => GalleryDefaults.MaxFullGalleryItemHeight;
-    }
-
-    public double MinFullGalleryItemHeight
-    {
-        get => GalleryDefaults.MinFullGalleryItemHeight;
-    }
-
-    public double MaxBottomGalleryItemHeight
-    {
-        get => GalleryDefaults.MaxBottomGalleryItemHeight;
-    }
-
-    public double MinBottomGalleryItemHeight
-    {
-        get => GalleryDefaults.MinBottomGalleryItemHeight;
-    }
-
-    public Thickness GalleryItemMargin
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #region Gallery Stretch IsChecked
-
-    public bool IsUniformBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUniformFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUniformMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUniformToFillBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUniformToFillFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUniformToFillMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsNoneBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsNoneFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsNoneMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsSquareBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsSquareFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsSquareMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillSquareBottomChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillSquareFullChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFillSquareMenuChecked
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion
-
-    #endregion Gallery
-
-    #region Commands
-
-    public ReactiveCommand<Unit, Unit>? ExitCommand { get; }
-    public ReactiveCommand<Unit, Unit>? MinimizeCommand { get; }
-    public ReactiveCommand<Unit, Unit>? MaximizeCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? RestoreCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleFullscreenCommand { get; }
-    public ReactiveCommand<Unit, Unit>? NextCommand { get; }
-    public ReactiveCommand<Unit, Unit>? NextButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? NextArrowButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? PreviousCommand { get; }
-    public ReactiveCommand<Unit, Unit>? PreviousButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? PreviousArrowButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? NextFolderCommand { get; }
-    public ReactiveCommand<Unit, Unit>? PreviousFolderCommand { get; }
-    public ReactiveCommand<Unit, Unit>? FirstCommand { get; }
-    public ReactiveCommand<Unit, Unit>? LastCommand { get; }
-    public ReactiveCommand<Unit, Unit>? Skip10Command { get; }
-    public ReactiveCommand<Unit, Unit>? Prev10Command { get; }
-    public ReactiveCommand<Unit, Unit>? Skip100Command { get; }
-    public ReactiveCommand<Unit, Unit>? Prev100Command { get; }
-    public ReactiveCommand<Unit, Unit>? OpenFileCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SaveFileCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SaveFileAsCommand { get; }
-    public ReactiveCommand<Unit, Unit>? OpenLastFileCommand { get; }
-    public ReactiveCommand<Unit, Unit>? PasteCommand { get; }
-    public ReactiveCommand<string, Unit>? CopyFileCommand { get; }
-    public ReactiveCommand<string, Unit>? CopyBase64Command { get; }
-    public ReactiveCommand<string, Unit>? CopyFilePathCommand { get; }
-    public ReactiveCommand<string, Unit>? FilePropertiesCommand { get; }
-    public ReactiveCommand<Unit, Unit>? CopyImageCommand { get; }
-    public ReactiveCommand<string, Unit>? CutCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ReloadCommand { get; }
-    public ReactiveCommand<string, Unit>? PrintCommand { get; }
-    public ReactiveCommand<string, Unit>? DeleteFileCommand { get; }
-    public ReactiveCommand<string, Unit>? RecycleFileCommand { get; }
-    public ReactiveCommand<Unit, Unit>? CloseMenuCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleFileMenuCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleImageMenuCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleSettingsMenuCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleToolsMenuCommand { get; }
-    public ReactiveCommand<string, Unit>? LocateOnDiskCommand { get; }
-    public ReactiveCommand<string, Unit>? OpenWithCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RenameCommand { get; }
-    public ReactiveCommand<Unit, Unit>? NewWindowCommand { get; }
-    public ReactiveCommand<string, Unit>? DuplicateFileCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleLoopingCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RotateLeftCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RotateLeftButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RotateRightCommand { get; }
-    public ReactiveCommand<string, Unit>? RotateToCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RotateRightButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? RotateRightWindowBorderButtonCommand { get; }
-    public ReactiveCommand<Unit, Unit>? FlipCommand { get; }
-    public ReactiveCommand<Unit, Unit>? StretchCommand { get; }
-    public ReactiveCommand<Unit, Unit>? CropCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ChangeAutoFitCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ChangeTopMostCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ChangeCtrlZoomCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleUsingTouchpadCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleUICommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleOpeningInSameWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ChangeBackgroundCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleBottomNavBarCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleBottomGalleryShownInHiddenUICommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ToggleFadeInButtonsOnHoverCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleTaskbarProgressCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowExifWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowAboutWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowSettingsWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowKeybindingsWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowBatchResizeWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowSingleImageResizeWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ShowEffectsWindowCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating0Command { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating1Command { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating2Command { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating3Command { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating4Command { get; }
-    public ReactiveCommand<Unit, Unit>? SetExifRating5Command { get; }
-    public ReactiveCommand<Unit, Unit>? OpenGoogleLinkCommand { get; }
-    public ReactiveCommand<Unit, Unit>? OpenBingLinkCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? OptimizeImageCommand { get; }
-    public ReactiveCommand<int, Unit>? ResizeCommand { get; }
-    public ReactiveCommand<int, Unit>? ConvertCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? SortFilesByNameCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesBySizeCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesByExtensionCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesByCreationTimeCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesByLastAccessTimeCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesRandomlyCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesAscendingCommand { get; }
-    public ReactiveCommand<Unit, Unit>? SortFilesDescendingCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ToggleGalleryCommand { get; }
-    public ReactiveCommand<Unit, Unit>? ToggleBottomGalleryCommand { get; }
-    public ReactiveCommand<Unit, Unit>? CloseGalleryCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ToggleScrollCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ToggleSubdirectoriesCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ColorPickerCommand { get; }
-
-    public ReactiveCommand<int, Unit>? SlideshowCommand { get; }
-
-    public ReactiveCommand<string, Unit>? SetAsWallpaperCommand { get; }
-    public ReactiveCommand<string, Unit>? SetAsWallpaperFilledCommand { get; }
-    public ReactiveCommand<string, Unit>? SetAsWallpaperStretchedCommand { get; }
-    public ReactiveCommand<string, Unit>? SetAsWallpaperTiledCommand { get; }
-    public ReactiveCommand<string, Unit>? SetAsWallpaperCenteredCommand { get; }
-
-    public ReactiveCommand<string, Unit>? SetAsLockScreenCommand { get; }
-
-    public ReactiveCommand<string, Unit>? GalleryItemStretchCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ResetSettingsCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? ShowSideBySideCommand { get; }
-
-    public ReactiveCommand<Unit, Unit>? RestartCommand { get; }
-    
-    public ReactiveCommand<Unit, Unit>? ShowSettingsFileCommand { get; }
-    
-    public ReactiveCommand<Unit, Unit>? ShowKeybindingsFileCommand { get; }
-    
-    public ReactiveCommand<Unit, Unit>? ToggleConstrainBackgroundColorCommand { get; }
-
-    #endregion Commands
-
-    #region Fields
-    
-    #region Sorting Order
-
-    public SortFilesBy SortOrder
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsAscending
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Sorting Order
-
-    #region Booleans
-
-    public bool ShouldCropBeEnabled
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool ShouldOptimizeImageBeEnabled
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsAvoidingZoomingOut
-    {
-        get;
-        set
-        {
-            Settings.Zoom.AvoidZoomingOut = value;
-            this.RaiseAndSetIfChanged(ref field, value);
-        }
-    }
-
-    public IImage? ChangeCtrlZoomImage
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsLoading
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUIShown
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsTopToolbarShown
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsBottomToolbarShown
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsShowingTaskbarProgress
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsFullscreen
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            ShouldRestore = IsFullscreen || IsMaximized;
-            ShouldMaximizeBeShown = !IsFullscreen && !IsMaximized;
-        }
-    }
-
-    public bool IsMaximized
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            ShouldRestore = IsFullscreen || IsMaximized;
-            ShouldMaximizeBeShown = !IsFullscreen && !IsMaximized;
-        }
-    }
-
-    public bool ShouldRestore
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool ShouldMaximizeBeShown
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    } = true;
-
-    public bool IsTopMost
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsConstrainingBackgroundColor
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsIncludingSubdirectories
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsScrollingEnabled
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsStretched
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.ImageScaling.StretchImage = value;
-            WindowResizing.SetSize(this);
-        }
-    }
-
-    public bool IsLooping
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsAutoFit
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsStayingCentered
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.WindowProperties.KeepCentered = value;
-        }
-    }
-
-    public bool IsOpeningInSameWindow
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.UIProperties.OpenInSameWindow = value;
-        }
-    }
-
-    public bool IsShowingConfirmationOnEsc
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.UIProperties.ShowConfirmationOnEsc = value;
-        }
-    }
-
-    public bool IsEditableTitlebarOpen
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsUsingTouchpad
-    {
-        get;
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.Zoom.IsUsingTouchPad = value;
-        }
-    }
-
-    public bool IsSingleImage
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Booleans
-    
-    public Brush? ImageBackground
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
     
     
-    public Brush? ConstrainedImageBackground
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-    
-    public Thickness RightControlOffSetMargin
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public Thickness TopScreenMargin
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public Thickness BottomScreenMargin
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public CornerRadius BottomCornerRadius
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public int BackgroundChoice
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double WindowMinSize
-    {
-        get { return SizeDefaults.WindowMinSize; }
-    }
-
-    public double TitlebarHeight
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double BottombarHeight
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public UserControl? CurrentView
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
     public ImageViewer? ImageViewer;
     public ImageViewer? ImageViewer;
 
 
-    public uint EXIFRating
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public int GetIndex
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double GetSlideshowSpeed
-    {
-        get;
-        set
-        {
-            var roundedValue = Math.Round(value, 2);
-            this.RaiseAndSetIfChanged(ref field, roundedValue);
-            Settings.UIProperties.SlideShowTimer = roundedValue;
-        }
-    }
-
-    public double GetNavSpeed
-    {
-        get => Math.Round(field, 2);
-        set
-        {
-            this.RaiseAndSetIfChanged(ref field, value);
-            Settings.UIProperties.NavSpeed = value;
-        }
-    }
-
-    public double GetZoomSpeed
-    {
-        get;
-        set
-        {
-            var roundedValue = Math.Round(value, 2);
-            this.RaiseAndSetIfChanged(ref field, roundedValue);
-            Settings.Zoom.ZoomSpeed = roundedValue;
-        }
-    }
-    
-    #region Window Properties
-
-    public SizeToContent SizeToContent
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool CanResize
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Window Properties
-
-    #region Size
 
 
-    public double TitleMaxWidth
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Size
-
-    #region Zoom
-
-    public double RotationAngle
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public double ZoomValue
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public ScrollBarVisibility ToggleScrollBarVisibility
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Zoom
-
-    #region Menus
-
-    public bool IsFileMenuVisible
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsImageMenuVisible
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsSettingsMenuVisible
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    public bool IsToolsMenuVisible
-    {
-        get;
-        set => this.RaiseAndSetIfChanged(ref field, value);
-    }
-
-    #endregion Menus
 
 
-    #endregion Fields
 
 
-    #region Methods
 
 
-    #region Tasks
-
-    private async Task ResizeImageByPercentage(int percentage) =>
-        await ConversionHelper.ResizeImageByPercentage(percentage, this).ConfigureAwait(false);
-
-    private async Task ConvertFileExtension(int index) =>
-        await ConversionHelper.ConvertFileExtension(index, this).ConfigureAwait(false);
-
-    private async Task CopyFileTask(string path) => 
-        await ClipboardFileOperations.CopyFileToClipboard(path, this).ConfigureAwait(false);
-
-    private static async Task CopyFilePathTask(string path) => 
-        await ClipboardTextOperations.CopyTextToClipboard(path).ConfigureAwait(false);
-
-    private async Task CopyBase64Task(string path) =>
-        await ClipboardImageOperations.CopyBase64ToClipboard(path, this).ConfigureAwait(false);
-
-    private async Task CutFileTask(string path) =>
-        await ClipboardFileOperations.CutFile(path, this).ConfigureAwait(false);
-
-    private async Task DeleteFileTask(string path) =>
-        await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(false, path, PlatformService)).ConfigureAwait(false);
-
-    private async Task RecycleFileTask(string path) =>
-        await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(true, path, PlatformService)).ConfigureAwait(false);
-
-    private async Task DuplicateFileTask(string path) =>
-        await ClipboardFileOperations.Duplicate(path, this).ConfigureAwait(false);
-
-    private async Task ShowFilePropertiesTask(string path) =>
-        await FileManager.ShowFileProperties(path, this).ConfigureAwait(false);
-
-    private async Task PrintTask(string path) =>
-        await FileManager.Print(path, this).ConfigureAwait(false);
-
-    private async Task OpenWithTask(string path) => 
-        await FileManager.OpenWith(path, this).ConfigureAwait(false);
-
-    private async Task LocateOnDiskTask(string path) =>
-        await FileManager.LocateOnDisk(path, this).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Fill).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperFilledTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Fill).ConfigureAwait(false);
-    
-    private async Task SetAsWallpaperFittedTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Fit).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperTiledTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Tile).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperStretchedTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Stretch).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperCenteredTask(string path) =>
-        await SetAsWallpaperTask(path, WallpaperStyle.Center).ConfigureAwait(false);
-
-    private async Task SetAsWallpaperTask(string path, WallpaperStyle style) =>
-        await WallpaperManager.SetAsWallpaper(path, style, this).ConfigureAwait(false);
-
-    private async Task SetAsLockScreenTask(string path) =>
-        await LockScreenHelper.SetAsLockScreenTask(path, this).ConfigureAwait(false);
-
-    private void SetGalleryItemStretch(string value) => GalleryHelper.SetGalleryItemStretch(value, this);
-
-    public async Task StartSlideShowTask(int milliseconds) =>
-        await Slideshow.StartSlideshow(this, milliseconds);
-
-    public async Task RotateToTask(string angle)
-    {
-        if (int.TryParse(angle, out var result))
-        {
-            await RotationNavigation.RotateTo(this, result);
-        }
-    }
-        
-    
-    #endregion
 
 
-    #endregion Methods
 }
 }

+ 151 - 0
src/PicView.Avalonia/ViewModels/MainWindowViewModel.cs

@@ -0,0 +1,151 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Media;
+using PicView.Avalonia.Functions;
+using PicView.Avalonia.UI;
+using PicView.Avalonia.WindowBehavior;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class MainWindowViewModel : IDisposable
+{
+    public BindableReactiveProperty<Brush?> ImageBackground { get; } = new();
+
+    public BindableReactiveProperty<Brush?> ConstrainedImageBackground { get; } = new();
+
+    public BindableReactiveProperty<Thickness> RightControlOffSetMargin { get; } = new();
+
+    public BindableReactiveProperty<Thickness> TopScreenMargin { get; } = new();
+
+    public BindableReactiveProperty<Thickness> BottomScreenMargin { get; } = new();
+
+    public BindableReactiveProperty<CornerRadius> BottomCornerRadius { get; } = new();
+
+    public BindableReactiveProperty<int> BackgroundChoice { get; } = new();
+
+    public BindableReactiveProperty<double> WindowMinSize { get; } = new();
+
+    public BindableReactiveProperty<double> TitlebarHeight { get; } = new();
+
+    public BindableReactiveProperty<double> BottombarHeight { get; } = new();
+    
+    public BindableReactiveProperty<SizeToContent> SizeToContent { get; } = new();
+    
+    public BindableReactiveProperty<ScrollBarVisibility> ToggleScrollBarVisibility { get; } = new();
+
+    public BindableReactiveProperty<bool> CanResize { get; } = new();
+
+    public BindableReactiveProperty<UserControl?> CurrentView { get; } = new();
+
+    public BindableReactiveProperty<bool> IsFileMenuVisible { get; } = new();
+
+    public BindableReactiveProperty<bool> IsImageMenuVisible { get; } = new();
+
+    public BindableReactiveProperty<bool> IsSettingsMenuVisible { get; } = new();
+
+    public BindableReactiveProperty<bool> IsToolsMenuVisible { get; } = new();
+    
+    public BindableReactiveProperty<double> TitleMaxWidth { get; } = new();
+    
+    public BindableReactiveProperty<bool> IsFullscreen { get; } = new();
+
+    public BindableReactiveProperty<bool> IsMaximized { get; } = new();
+
+    public BindableReactiveProperty<bool> ShouldRestore { get; } = new();
+
+    public BindableReactiveProperty<bool> ShouldMaximizeBeShown { get; } = new(true);
+
+    public BindableReactiveProperty<bool> IsLoadingIndicatorShown { get; } = new();
+
+    public BindableReactiveProperty<bool> IsUIShown { get; } = new();
+    public BindableReactiveProperty<bool> IsTopToolbarShown { get; } = new();
+
+    public BindableReactiveProperty<bool> IsBottomToolbarShown { get; } = new();
+
+    public BindableReactiveProperty<bool> IsEditableTitlebarOpen { get; } = new();
+    
+    public BindableReactiveProperty<IImage?> ChangeCtrlZoomImage { get; } = new();
+
+    public void LayoutButtonSubscription()
+    {
+        Observable.EveryValueChanged(this, x => x.IsMaximized.CurrentValue, UIHelper.GetFrameProvider)
+            .Subscribe(_ => SetButtonValues());
+        Observable.EveryValueChanged(this, x => x.IsFullscreen.CurrentValue, UIHelper.GetFrameProvider)
+            .Subscribe(_ => SetButtonValues());
+    }
+    
+    #region Menus
+    
+    public ReactiveCommand CloseMenuCommand { get; } = new(CloseMenus);
+    
+    public ReactiveCommand ToggleFileMenuCommand { get; } = new(ToggleFileMenu);
+    public ReactiveCommand ToggleImageMenuCommand { get; } = new(ToggleImageMenu);
+    public ReactiveCommand ToggleSettingsMenuCommand { get; } = new(ToggleSettingsMenu);
+    public ReactiveCommand ToggleToolsMenuCommand { get; } = new(ToggleToolsMenu);
+
+    private static void CloseMenus(Unit unit) => MenuManager.CloseMenus(UIHelper.GetMainView.DataContext as MainViewModel);
+    
+    private static void ToggleFileMenu(Unit unit) => MenuManager.ToggleFileMenu(UIHelper.GetMainView.DataContext as MainViewModel);
+    private static void ToggleImageMenu(Unit unit) => MenuManager.ToggleImageMenu(UIHelper.GetMainView.DataContext as MainViewModel);
+    private static void ToggleSettingsMenu(Unit unit) => MenuManager.ToggleSettingsMenu(UIHelper.GetMainView.DataContext as MainViewModel);
+    private static void ToggleToolsMenu(Unit unit) => MenuManager.ToggleToolsMenu(UIHelper.GetMainView.DataContext as MainViewModel);
+
+    #endregion Menus
+    
+    public ReactiveCommand ExitCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Close();
+    });
+    
+    public ReactiveCommand MaximizeCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Maximize();
+    });
+    
+    public ReactiveCommand MinimizeCommand { get; } = new(async (_, _) =>
+    {
+        await WindowFunctions.Minimize();
+    });
+    
+    public ReactiveCommand RestoreCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Restore();
+    });
+    
+    public ReactiveCommand ToggleFullscreenCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleFullscreen();
+    });
+    
+
+    private void SetButtonValues()
+    {
+        ShouldRestore.Value = IsFullscreen.CurrentValue || IsMaximized.CurrentValue;
+        ShouldMaximizeBeShown.Value = !IsFullscreen.CurrentValue && !IsMaximized.CurrentValue;
+    }
+
+    public void Dispose()
+    {
+        Disposable.Dispose(ImageBackground,
+            ConstrainedImageBackground,
+            RightControlOffSetMargin,
+            TopScreenMargin,
+            BottomScreenMargin,
+            BottomCornerRadius,
+            BackgroundChoice,
+            WindowMinSize,
+            TitlebarHeight,
+            BottombarHeight,
+            SizeToContent,
+            CanResize,
+            CurrentView,
+            TitleMaxWidth,
+            IsLoadingIndicatorShown,
+            IsUIShown,
+            IsTopToolbarShown,
+            IsBottomToolbarShown,
+            IsEditableTitlebarOpen);
+    }
+}

+ 101 - 0
src/PicView.Avalonia/ViewModels/NavigationViewModel.cs

@@ -0,0 +1,101 @@
+using PicView.Avalonia.Navigation;
+using PicView.Avalonia.UI;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+// TODO: Move this to Core by using interfaces
+public class NavigationViewModel : IDisposable
+{
+    // Reload
+    public ReactiveCommand ReloadCommand { get; } = new(async (_, _) =>
+    {
+        await ErrorHandling.ReloadAsync(UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    // Next
+    public ReactiveCommand NextCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.Iterate(next: true).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand NextButtonCommand { get; } = new(async (_, _) =>
+    {
+        await UIHelper.NextButtonNavigation();
+    });
+    
+    public ReactiveCommand NextArrowButtonCommand { get; } = new(async (_, _) =>
+    {
+        await UIHelper.NextArrowButtonNavigation();
+    });
+    
+    public ReactiveCommand NextFolderCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateBetweenDirectories(next: true).ConfigureAwait(false);
+    });
+    
+    // Prev
+    public ReactiveCommand PreviousCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.Iterate(next: false).ConfigureAwait(false);
+    });
+    public ReactiveCommand PreviousButtonCommand { get; } = new(async (_, _) =>
+    {
+        await UIHelper.PreviousButtonNavigation();
+    });
+    public ReactiveCommand PreviousArrowButtonCommand { get; } = new(async (_, _) =>
+    {
+        await UIHelper.PreviousArrowButtonNavigation();
+    });
+
+    public ReactiveCommand PreviousFolderCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateBetweenDirectories(next: false).ConfigureAwait(false);
+    });
+    
+    // Skip
+    public ReactiveCommand FirstCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateFirstOrLast(last: false).ConfigureAwait(false);
+    });
+    public ReactiveCommand LastCommand { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateFirstOrLast(last: true).ConfigureAwait(false);
+    });
+    public ReactiveCommand Skip10Command { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateIncrements(next: true, true, false).ConfigureAwait(false);
+    });
+    public ReactiveCommand Skip100Command { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateIncrements(next: true, false, true).ConfigureAwait(false);
+    });
+    public ReactiveCommand Prev10Command { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateIncrements(next: false, true, false).ConfigureAwait(false);
+    });
+
+    public ReactiveCommand Prev100Command { get; } = new(async (_, _) =>
+    {
+        await NavigationManager.NavigateIncrements(next: false, false, true).ConfigureAwait(false);
+    });
+    
+    public void Dispose()
+    {
+        Disposable.Dispose(ReloadCommand,
+            NextCommand,
+            NextButtonCommand,
+            NextArrowButtonCommand,
+            NextFolderCommand,
+            PreviousCommand,
+            PreviousButtonCommand,
+            PreviousArrowButtonCommand,
+            PreviousFolderCommand,
+            FirstCommand,
+            LastCommand,
+            Skip10Command,
+            Prev10Command,
+            Skip100Command,
+            Prev100Command);
+    }
+}

+ 326 - 0
src/PicView.Avalonia/ViewModels/ToolsViewModel.cs

@@ -0,0 +1,326 @@
+using PicView.Avalonia.Clipboard;
+using PicView.Avalonia.FileSystem;
+using PicView.Avalonia.Functions;
+using PicView.Avalonia.ImageTransformations.Rotation;
+using PicView.Avalonia.Navigation;
+using PicView.Avalonia.SettingsManagement;
+using PicView.Avalonia.UI;
+using PicView.Avalonia.Wallpaper;
+using R3;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class ToolsViewModel : IDisposable
+{
+    public ReactiveCommand ShowSettingsFileCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ShowSettingsFile();
+    });
+        
+    public ReactiveCommand ShowKeybindingsFileCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ShowSettingsFile();
+    });
+    
+    // Open related
+    public ReactiveCommand OpenFileCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Open();
+    });
+    
+    public ReactiveCommand OpenLastFileCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.OpenLastFile();
+    });
+    
+    public ReactiveCommand<string> OpenWithCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await FileManager.OpenWith(path, vm).ConfigureAwait(false);
+        }
+    });
+    // Save related
+    public ReactiveCommand<string> SaveFileCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Save();
+    });
+    
+    public ReactiveCommand<string> SaveFileAsCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SaveAs();
+    });
+    
+    // File Tasks
+    public ReactiveCommand<string> RecycleFileCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(true, path, vm.PlatformService)).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> DeleteFilePermanentlyCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await Task.Run(() => FileManager.DeleteFileWithOptionalDialog(false, path, vm.PlatformService)).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> LocateOnDiskCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await FileManager.LocateOnDisk(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> FilePropertiesCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await FileManager.ShowFileProperties(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> PrintCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await FileManager.Print(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> RenameCommand { get; } = new(async (_, _) =>
+    {
+        await Task.Run(FunctionsMapper.Rename);
+    });
+
+    
+    // Copy related
+    public ReactiveCommand<string> PasteCommand { get; } = new(async (_, _) =>
+    {
+        await Task.Run(FunctionsMapper.Paste);
+    });
+    
+    public ReactiveCommand<string> DuplicateFileCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await ClipboardFileOperations.Duplicate(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> CopyImageCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.CopyImage().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> CopyFileCommand { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await ClipboardFileOperations.CopyFileToClipboard(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    public ReactiveCommand<string> CopyFilePathCommand { get; } = new(async (path, _) =>
+    {
+        await ClipboardTextOperations.CopyTextToClipboard(path).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> CopyBase64Command { get; } = new(async (path, _) =>
+    {
+        if (UIHelper.GetMainView.DataContext is MainViewModel vm)
+        {
+            await ClipboardImageOperations.CopyBase64ToClipboard(path, vm).ConfigureAwait(false);
+        }
+    });
+    
+    // Settings
+    public ReactiveCommand ChangeAutoFitCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.AutoFitWindow().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand ChangeTopMostCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SetTopMost().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand ToggleSubdirectoriesCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleSubdirectories().ConfigureAwait(false);
+    });
+
+    public ReactiveCommand ToggleLoopingCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleLooping().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand ResetSettingsCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ResetSettings().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand RestartCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Restart().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand ToggleOpeningInSameWindowCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleOpeningInSameWindow().ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand ToggleUsingTouchPadCommand { get; } = new(async (_, _) =>
+    {
+        await SettingsUpdater.ToggleUsingTouchpad(UIHelper.GetMainView.DataContext as MainViewModel);
+    });
+    
+    // UI
+    public ReactiveCommand ToggleUICommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleInterface();
+    });
+    
+    public ReactiveCommand ToggleBottomNavBarCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleBottomToolbar();
+    });
+    
+    public ReactiveCommand ToggleBottomGalleryShownInHiddenUICommand { get; } = new(async (_, _) =>
+    {
+        await HideInterfaceLogic.ToggleBottomGalleryShownInHiddenUI(UIHelper.GetMainView.DataContext as MainViewModel);
+    });
+    
+    public ReactiveCommand ToggleFadeInButtonsOnHoverCommand { get; } = new(async (_, _) =>
+    {
+        await HideInterfaceLogic.ToggleFadeInButtonsOnHover(UIHelper.GetMainView.DataContext as MainViewModel);
+    });
+    
+    public ReactiveCommand ChangeCtrlZoomCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ChangeCtrlZoom();
+    });
+    
+    public ReactiveCommand ToggleTaskbarProgressCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleTaskbarProgress();
+    });
+    
+    public ReactiveCommand ToggleConstrainBackgroundColorCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleConstrainBackgroundColor();
+    });
+    
+    // Image related
+    public ReactiveCommand RotateLeftCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.RotateLeft();
+    });
+    public ReactiveCommand RotateLeftButtonCommand { get; } = new(async (_, _) =>
+    {
+        await RotationNavigation.RotateLeft(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.RotateLeftButton);
+    });
+    
+    public ReactiveCommand RotateRightCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.RotateRight();
+    });
+    public ReactiveCommand RotateRightButtonCommand { get; } = new(async (_, _) =>
+    {
+        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.RotateLeftButton);
+    });
+    
+    public ReactiveCommand RotateRightWindowBorderButtonCommand { get; } = new(async (_, _) =>
+    {
+        await RotationNavigation.RotateRight(UIHelper.GetMainView.DataContext as MainViewModel, RotationButton.WindowBorderButton);
+    });
+    
+    public ReactiveCommand FlipCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Flip();
+    });
+    
+    public ReactiveCommand StretchCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Stretch();
+    });
+    
+    public ReactiveCommand CropCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.Crop();
+    });
+    
+    public ReactiveCommand ToggleScrollCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ToggleScroll();
+    });
+    
+    public ReactiveCommand OptimizeImageCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.OptimizeImage();
+    });
+    
+    public ReactiveCommand ChangeBackgroundCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.ChangeBackground();
+    });
+    
+    public ReactiveCommand ShowSideBySideCommand { get; } = new(async (_, _) =>
+    {
+        await FunctionsMapper.SideBySide();
+    });
+    
+    public async Task StartSlideShowTask(int milliseconds) =>
+        await Slideshow.StartSlideshow(UIHelper.GetMainView.DataContext as MainViewModel, milliseconds);
+    
+    public async Task RotateTask(int angle) =>
+        await RotationNavigation.RotateTo(UIHelper.GetMainView.DataContext as MainViewModel, angle);
+
+    public async Task StretchedCommand() =>
+        await SettingsUpdater.ToggleStretch(UIHelper.GetMainView.DataContext as MainViewModel);
+    
+    // Wallpaper
+    public ReactiveCommand<string> SetAsWallpaperCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> SetAsWallpaperTiledCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Tile, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> SetAsWallpaperStretchedCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Stretch, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> SetAsWallpaperCenteredCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Center, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> SetAsWallpaperFilledCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fill, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+    
+    public ReactiveCommand<string> SetAsWallpaperFittedCommand { get; } = new(async (path, _) =>
+    {
+        await WallpaperManager.SetAsWallpaper(path, WallpaperStyle.Fit, UIHelper.GetMainView.DataContext as MainViewModel).ConfigureAwait(false);
+    });
+
+    public void Dispose()
+    {
+        Disposable.Dispose(SetAsWallpaperCommand,
+            SetAsWallpaperFilledCommand,
+            SetAsWallpaperStretchedCommand,
+            SetAsWallpaperCenteredCommand,
+            SetAsWallpaperFilledCommand,
+            SetAsWallpaperFittedCommand);
+    }
+}

+ 65 - 0
src/PicView.Avalonia/ViewModels/WindowViewModel.cs

@@ -0,0 +1,65 @@
+using PicView.Avalonia.UI;
+using PicView.Core.ProcessHandling;
+
+namespace PicView.Avalonia.ViewModels;
+
+public class WindowViewModel
+{
+    public void NewWindow() => ProcessHelper.StartNewProcess();
+    
+    public void ShowExifWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowExifWindow();
+        }
+    }
+
+    public void ShowSettingsWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowSettingsWindow();
+        }
+    }
+
+    public void ShowKeybindingsWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowKeybindingsWindow();
+        }
+    }
+
+    public void ShowAboutWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowAboutWindow();
+        }
+    }
+
+    public void ShowBatchResizeWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowBatchResizeWindow();
+        }
+    }
+
+    public void ShowSingleImageResizeWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowSingleImageResizeWindow();
+        }
+    }
+
+    public void ShowEffectsWindow()
+    {
+        if (UIHelper.TryGetMainViewModel(out var vm))
+        {
+            vm.PlatformWindowService.ShowEffectsWindow();
+        }
+    }
+}

+ 8 - 5
src/PicView.Avalonia/Views/AboutView.axaml

@@ -126,7 +126,7 @@
             HorizontalAlignment="Center"
             HorizontalAlignment="Center"
             IsVisible="{Binding IsVisible, ElementName=SpinWaiter}"
             IsVisible="{Binding IsVisible, ElementName=SpinWaiter}"
             Margin="0,75,0,0"
             Margin="0,75,0,0"
-            Text="{CompiledBinding Translation.Downloading}"
+            Text="{CompiledBinding Translation.Downloading.Value}"
             VerticalAlignment="Center" />
             VerticalAlignment="Center" />
 
 
         <StackPanel Margin="0,10,0,10" x:Name="ParentContainer">
         <StackPanel Margin="0,10,0,10" x:Name="ParentContainer">
@@ -145,7 +145,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Foreground="{StaticResource SecondaryTextColor}"
                         Foreground="{StaticResource SecondaryTextColor}"
-                        Text="{CompiledBinding Translation.GithubRepo}" />
+                        Text="{CompiledBinding Translation.GithubRepo.Value,
+                                               Mode=OneWay}" />
                     <Path
                     <Path
                         Data="M256,32C132.3,32,32,134.9,32,261.7c0,101.5,64.2,187.5,153.2,217.9a17.56,17.56,0,0,0,3.8.4c8.3,0,11.5-6.1,11.5-11.4,0-5.5-.2-19.9-.3-39.1a102.4,102.4,0,0,1-22.6,2.7c-43.1,0-52.9-33.5-52.9-33.5-10.2-26.5-24.9-33.6-24.9-33.6-19.5-13.7-.1-14.1,1.4-14.1h.1c22.5,2,34.3,23.8,34.3,23.8,11.2,19.6,26.2,25.1,39.6,25.1a63,63,0,0,0,25.6-6c2-14.8,7.8-24.9,14.2-30.7-49.7-5.8-102-25.5-102-113.5,0-25.1,8.7-45.6,23-61.6-2.3-5.8-10-29.2,2.2-60.8a18.64,18.64,0,0,1,5-.5c8.1,0,26.4,3.1,56.6,24.1a208.21,208.21,0,0,1,112.2,0c30.2-21,48.5-24.1,56.6-24.1a18.64,18.64,0,0,1,5,.5c12.2,31.6,4.5,55,2.2,60.8,14.3,16.1,23,36.6,23,61.6,0,88.2-52.4,107.6-102.3,113.3,8,7.1,15.2,21.1,15.2,42.5,0,30.7-.3,55.5-.3,63,0,5.4,3.1,11.5,11.4,11.5a19.35,19.35,0,0,0,4-.4C415.9,449.2,480,363.1,480,261.7,480,134.9,379.7,32,256,32Z"
                         Data="M256,32C132.3,32,32,134.9,32,261.7c0,101.5,64.2,187.5,153.2,217.9a17.56,17.56,0,0,0,3.8.4c8.3,0,11.5-6.1,11.5-11.4,0-5.5-.2-19.9-.3-39.1a102.4,102.4,0,0,1-22.6,2.7c-43.1,0-52.9-33.5-52.9-33.5-10.2-26.5-24.9-33.6-24.9-33.6-19.5-13.7-.1-14.1,1.4-14.1h.1c22.5,2,34.3,23.8,34.3,23.8,11.2,19.6,26.2,25.1,39.6,25.1a63,63,0,0,0,25.6-6c2-14.8,7.8-24.9,14.2-30.7-49.7-5.8-102-25.5-102-113.5,0-25.1,8.7-45.6,23-61.6-2.3-5.8-10-29.2,2.2-60.8a18.64,18.64,0,0,1,5-.5c8.1,0,26.4,3.1,56.6,24.1a208.21,208.21,0,0,1,112.2,0c30.2-21,48.5-24.1,56.6-24.1a18.64,18.64,0,0,1,5,.5c12.2,31.6,4.5,55,2.2,60.8,14.3,16.1,23,36.6,23,61.6,0,88.2-52.4,107.6-102.3,113.3,8,7.1,15.2,21.1,15.2,42.5,0,30.7-.3,55.5-.3,63,0,5.4,3.1,11.5,11.4,11.5a19.35,19.35,0,0,0,4-.4C415.9,449.2,480,363.1,480,261.7,480,134.9,379.7,32,256,32Z"
                         Fill="{StaticResource SecondaryTextColor}"
                         Fill="{StaticResource SecondaryTextColor}"
@@ -178,7 +179,8 @@
                 HorizontalAlignment="Center"
                 HorizontalAlignment="Center"
                 Margin="0,2,0,2"
                 Margin="0,2,0,2"
                 NavigateUri="https://github.com/Ruben2776/PicView?tab=License-1-ov-file#readme"
                 NavigateUri="https://github.com/Ruben2776/PicView?tab=License-1-ov-file#readme"
-                ToolTip.Tip="{CompiledBinding Translation.ViewLicenseFile}">
+                ToolTip.Tip="{CompiledBinding Translation.ViewLicenseFile.Value,
+                                              Mode=OneWay}">
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
                     Foreground="{StaticResource SecondaryTextColor}"
                     Foreground="{StaticResource SecondaryTextColor}"
@@ -190,7 +192,7 @@
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
                 HorizontalAlignment="Center"
                 HorizontalAlignment="Center"
                 Margin="0,25,0,10">
                 Margin="0,25,0,10">
-                <Run FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto" Text="{CompiledBinding Translation.Version}" />
+                <Run FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto" Text="{CompiledBinding Translation.Version.Value, Mode=OneWay}" />
                 <Run Text="" x:Name="AppVersion" />
                 <Run Text="" x:Name="AppVersion" />
             </TextBlock>
             </TextBlock>
 
 
@@ -202,7 +204,8 @@
                 <TextBlock
                 <TextBlock
                     Classes="txt"
                     Classes="txt"
                     Foreground="{Binding Path=Foreground, ElementName=UpdateButton}"
                     Foreground="{Binding Path=Foreground, ElementName=UpdateButton}"
-                    Text="{CompiledBinding Translation.CheckForUpdates}" />
+                    Text="{CompiledBinding Translation.CheckForUpdates.Value,
+                                           Mode=OneWay}" />
             </Button>
             </Button>
 
 
             <TextBlock
             <TextBlock

+ 20 - 20
src/PicView.Avalonia/Views/AppearanceView.axaml

@@ -20,7 +20,7 @@
             FontSize="14"
             FontSize="14"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,20,0,20"
             Margin="0,20,0,20"
-            Text="{CompiledBinding Translation.Theme,
+            Text="{CompiledBinding Translation.Theme.Value,
                                    Mode=OneWay}" />
                                    Mode=OneWay}" />
 
 
         <ComboBox
         <ComboBox
@@ -33,11 +33,11 @@
             Padding="5,7,0,7"
             Padding="5,7,0,7"
             Width="300"
             Width="300"
             x:Name="ThemeBox">
             x:Name="ThemeBox">
-            <ComboBoxItem Content="{CompiledBinding Translation.DarkTheme, Mode=OneWay}" x:Name="DarkThemeBox" />
+            <ComboBoxItem Content="{CompiledBinding Translation.DarkTheme.Value, Mode=OneWay}" x:Name="DarkThemeBox" />
             <ComboBoxItem x:Name="LightThemeBox">
             <ComboBoxItem x:Name="LightThemeBox">
                 <TextBlock Classes="txt">
                 <TextBlock Classes="txt">
                     <TextBlock.Inlines>
                     <TextBlock.Inlines>
-                        <Run Text="{CompiledBinding Translation.LightTheme, Mode=OneWay}" />
+                        <Run Text="{CompiledBinding Translation.LightTheme.Value, Mode=OneWay}" />
                         <Run FontStyle="Italic" Text=" (beta)" />
                         <Run FontStyle="Italic" Text=" (beta)" />
                     </TextBlock.Inlines>
                     </TextBlock.Inlines>
                 </TextBlock>
                 </TextBlock>
@@ -45,7 +45,7 @@
             <ComboBoxItem x:Name="GlassThemeBox">
             <ComboBoxItem x:Name="GlassThemeBox">
                 <TextBlock Classes="txt">
                 <TextBlock Classes="txt">
                     <TextBlock.Inlines>
                     <TextBlock.Inlines>
-                        <Run Text="{CompiledBinding Translation.GlassTheme, Mode=OneWay}" />
+                        <Run Text="{CompiledBinding Translation.GlassTheme.Value, Mode=OneWay}" />
                         <Run FontStyle="Italic" Text=" (beta)" />
                         <Run FontStyle="Italic" Text=" (beta)" />
                     </TextBlock.Inlines>
                     </TextBlock.Inlines>
                 </TextBlock>
                 </TextBlock>
@@ -54,14 +54,14 @@
 
 
         <Button
         <Button
             Classes="errorHover"
             Classes="errorHover"
-            Command="{Binding RestartCommand}"
+            Command="{Binding Tools.RestartCommand}"
             Margin="0,0,0,20">
             Margin="0,0,0,20">
             <TextBlock
             <TextBlock
                 Classes="txt"
                 Classes="txt"
                 FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
                 FontFamily="/Assets/Fonts/Roboto-Bold.ttf#Roboto"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Foreground="{StaticResource SecondaryTextColor}"
                 Padding="5"
                 Padding="5"
-                Text="{CompiledBinding Translation.ChangingThemeRequiresRestart,
+                Text="{CompiledBinding Translation.ChangingThemeRequiresRestart.Value,
                                        Mode=OneWay}" />
                                        Mode=OneWay}" />
         </Button>
         </Button>
 
 
@@ -71,7 +71,7 @@
             FontSize="14"
             FontSize="14"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,5,0,20"
             Margin="0,5,0,20"
-            Text="{CompiledBinding Translation.HighlightColor,
+            Text="{CompiledBinding Translation.HighlightColor.Value,
                                    Mode=OneWay}" />
                                    Mode=OneWay}" />
 
 
         <WrapPanel
         <WrapPanel
@@ -178,7 +178,7 @@
             FontSize="14"
             FontSize="14"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,20,0,10"
             Margin="0,20,0,10"
-            Text="{CompiledBinding Translation.ChangeBackground,
+            Text="{CompiledBinding Translation.ChangeBackground.Value,
                                    Mode=OneWay}" />
                                    Mode=OneWay}" />
 
 
         <WrapPanel
         <WrapPanel
@@ -284,8 +284,8 @@
             Background="Transparent"
             Background="Transparent"
             BorderThickness="0"
             BorderThickness="0"
             Classes="altHover"
             Classes="altHover"
-            Command="{CompiledBinding ToggleConstrainBackgroundColorCommand}"
-            IsChecked="{CompiledBinding IsConstrainingBackgroundColor}"
+            Command="{CompiledBinding Tools.ToggleConstrainBackgroundColorCommand}"
+            IsChecked="{CompiledBinding SettingsViewModel.IsConstrainingBackgroundColor.Value}"
             Margin="0,0,0,10"
             Margin="0,0,0,10"
             Width="300">
             Width="300">
             <TextBlock
             <TextBlock
@@ -294,7 +294,7 @@
                 Margin="0"
                 Margin="0"
                 MaxWidth="240"
                 MaxWidth="240"
                 Padding="0,1,5,0"
                 Padding="0,1,5,0"
-                Text="{CompiledBinding Translation.ConstrainBackgroundToImage,
+                Text="{CompiledBinding Translation.ConstrainBackgroundToImage.Value,
                                        Mode=OneWay}" />
                                        Mode=OneWay}" />
         </ToggleButton>
         </ToggleButton>
 
 
@@ -304,13 +304,13 @@
             FontSize="14"
             FontSize="14"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,20,0,10"
             Margin="0,20,0,10"
-            Text="{CompiledBinding Translation.InterfaceConfiguration,
+            Text="{CompiledBinding Translation.InterfaceConfiguration.Value,
                                    Mode=OneWay}" />
                                    Mode=OneWay}" />
         <Button
         <Button
             Background="Transparent"
             Background="Transparent"
             BorderThickness="0"
             BorderThickness="0"
             Classes="altHover"
             Classes="altHover"
-            Command="{CompiledBinding ToggleBottomNavBarCommand}"
+            Command="{CompiledBinding Tools.ToggleBottomNavBarCommand}"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,0,0,10"
             Margin="0,0,0,10"
             Padding="0,10,0,10"
             Padding="0,10,0,10"
@@ -361,7 +361,7 @@
                     Margin="0"
                     Margin="0"
                     MaxWidth="240"
                     MaxWidth="240"
                     Padding="0,1,5,0"
                     Padding="0,1,5,0"
-                    Text="{CompiledBinding Translation.IsShowingBottomToolbar,
+                    Text="{CompiledBinding Translation.IsShowingBottomToolbar.Value,
                                            Mode=OneWay}" />
                                            Mode=OneWay}" />
             </StackPanel>
             </StackPanel>
         </Button>
         </Button>
@@ -371,14 +371,14 @@
             Background="Transparent"
             Background="Transparent"
             BorderThickness="0"
             BorderThickness="0"
             Classes="altHover"
             Classes="altHover"
-            Command="{CompiledBinding ToggleUICommand}"
+            Command="{CompiledBinding Tools.ToggleUICommand}"
             Margin="0,0,0,10"
             Margin="0,0,0,10"
             Padding="0,10,0,10"
             Padding="0,10,0,10"
             Width="300">
             Width="300">
             <StackPanel Orientation="Horizontal">
             <StackPanel Orientation="Horizontal">
                 <Image
                 <Image
                     Height="18"
                     Height="18"
-                    IsVisible="{CompiledBinding IsUIShown}"
+                    IsVisible="{CompiledBinding MainWindow.IsUIShown.Value}"
                     Margin="10,0,10,0"
                     Margin="10,0,10,0"
                     Width="18">
                     Width="18">
                     <Image.Source>
                     <Image.Source>
@@ -435,7 +435,7 @@
                 </Image>
                 </Image>
                 <Image
                 <Image
                     Height="18"
                     Height="18"
-                    IsVisible="{CompiledBinding !IsUIShown}"
+                    IsVisible="{CompiledBinding !MainWindow.IsUIShown.Value}"
                     Margin="10,0,10,0"
                     Margin="10,0,10,0"
                     Width="18">
                     Width="18">
                     <DrawingImage>
                     <DrawingImage>
@@ -469,7 +469,7 @@
                     Margin="0"
                     Margin="0"
                     MaxWidth="240"
                     MaxWidth="240"
                     Padding="0,1,5,0"
                     Padding="0,1,5,0"
-                    Text="{CompiledBinding Translation.IsShowingUI,
+                    Text="{CompiledBinding Translation.IsShowingUI.Value,
                                            Mode=OneWay}" />
                                            Mode=OneWay}" />
             </StackPanel>
             </StackPanel>
         </Button>
         </Button>
@@ -478,7 +478,7 @@
             Background="Transparent"
             Background="Transparent"
             BorderThickness="0"
             BorderThickness="0"
             Classes="altHover"
             Classes="altHover"
-            Command="{CompiledBinding ToggleFadeInButtonsOnHoverCommand}"
+            Command="{CompiledBinding Tools.ToggleFadeInButtonsOnHoverCommand}"
             Foreground="{StaticResource SecondaryTextColor}"
             Foreground="{StaticResource SecondaryTextColor}"
             Margin="0,0,0,10"
             Margin="0,0,0,10"
             Padding="0,10,0,10"
             Padding="0,10,0,10"
@@ -521,7 +521,7 @@
                     Margin="0"
                     Margin="0"
                     MaxWidth="240"
                     MaxWidth="240"
                     Padding="0,1,5,0"
                     Padding="0,1,5,0"
-                    Text="{CompiledBinding Translation.IsShowingFadingUIButtons,
+                    Text="{CompiledBinding Translation.IsShowingFadingUIButtons.Value,
                                            Mode=OneWay}" />
                                            Mode=OneWay}" />
             </StackPanel>
             </StackPanel>
         </Button>
         </Button>

+ 4 - 6
src/PicView.Avalonia/Views/AppearanceView.axaml.cs

@@ -1,13 +1,12 @@
-using System.Reactive.Disposables;
 using Avalonia.Controls;
 using Avalonia.Controls;
 using Avalonia.Interactivity;
 using Avalonia.Interactivity;
 using Avalonia.Media;
 using Avalonia.Media;
 using PicView.Avalonia.ColorManagement;
 using PicView.Avalonia.ColorManagement;
 using PicView.Avalonia.Gallery;
 using PicView.Avalonia.Gallery;
+using PicView.Avalonia.UI;
 using PicView.Avalonia.ViewModels;
 using PicView.Avalonia.ViewModels;
 using PicView.Core.ColorHandling;
 using PicView.Core.ColorHandling;
-using PicView.Core.Localization;
-using ReactiveUI;
+using R3;
 
 
 namespace PicView.Avalonia.Views;
 namespace PicView.Avalonia.Views;
 
 
@@ -114,10 +113,9 @@ public partial class AppearanceView : UserControl
         CheckerboardButton.Background = BackgroundManager.CreateCheckerboardBrush(default, default,10);
         CheckerboardButton.Background = BackgroundManager.CreateCheckerboardBrush(default, default,10);
         CheckerboardAltButton.Background = BackgroundManager.CreateCheckerboardBrushAlt(25);
         CheckerboardAltButton.Background = BackgroundManager.CreateCheckerboardBrushAlt(25);
         
         
-        // Subscribe to background color changes with ReactiveUI
-        vm.WhenAnyValue(x => x.BackgroundChoice)
+        Observable.EveryValueChanged(vm.MainWindow, x => x.BackgroundChoice, UIHelper.GetFrameProvider)
             .Subscribe(_ => SetBackgroundTheme(Settings.UIProperties.BgColorChoice))
             .Subscribe(_ => SetBackgroundTheme(Settings.UIProperties.BgColorChoice))
-            .DisposeWith(_disposables);
+            .AddTo(_disposables);
     }
     }
     
     
     // New method to update color buttons with values from ColorManager
     // New method to update color buttons with values from ColorManager

+ 80 - 67
src/PicView.Avalonia/Views/BatchResizeView.axaml

@@ -25,7 +25,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.SourceFolder}"
+                        Text="{CompiledBinding Translation.SourceFolder.Value,
+                                               Mode=OneWay}"
                         Width="130" />
                         Width="130" />
                     <customControls:FuncTextBox
                     <customControls:FuncTextBox
                         Classes="hover TStyle"
                         Classes="hover TStyle"
@@ -47,7 +48,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.OutputFolder}"
+                        Text="{CompiledBinding Translation.OutputFolder.Value,
+                                               Mode=OneWay}"
                         Width="130" />
                         Width="130" />
                     <customControls:FuncTextBox
                     <customControls:FuncTextBox
                         Classes="hover TStyle"
                         Classes="hover TStyle"
@@ -69,7 +71,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.ConvertTo,
+                        Text="{CompiledBinding Translation.ConvertTo.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         Width="130" />
                         Width="130" />
                     <ComboBox
                     <ComboBox
@@ -85,7 +87,7 @@
                         SelectedItem="NoConversion"
                         SelectedItem="NoConversion"
                         Width="195"
                         Width="195"
                         x:Name="ConversionComboBox">
                         x:Name="ConversionComboBox">
-                        <ComboBoxItem Content="{CompiledBinding Translation.NoConversion, Mode=OneWay}" x:Name="NoConversion" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.NoConversion.Value, Mode=OneWay}" x:Name="NoConversion" />
                         <ComboBoxItem Content=".png" x:Name="PngItem" />
                         <ComboBoxItem Content=".png" x:Name="PngItem" />
                         <ComboBoxItem Content=".jpg" x:Name="JpgItem" />
                         <ComboBoxItem Content=".jpg" x:Name="JpgItem" />
                         <ComboBoxItem Content=".webp" x:Name="WebpItem" />
                         <ComboBoxItem Content=".webp" x:Name="WebpItem" />
@@ -99,7 +101,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Compression,
+                        Text="{CompiledBinding Translation.Compression.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         Width="130" />
                         Width="130" />
                     <ComboBox
                     <ComboBox
@@ -115,9 +117,9 @@
                         SelectedItem="Lossless"
                         SelectedItem="Lossless"
                         Width="195"
                         Width="195"
                         x:Name="CompressionComboBox">
                         x:Name="CompressionComboBox">
-                        <ComboBoxItem Content="{CompiledBinding Translation.Lossless, Mode=OneWay}" x:Name="Lossless" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.Lossy, Mode=OneWay}" x:Name="Lossy" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.None, Mode=OneWay}" x:Name="None" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.Lossless.Value, Mode=OneWay}" x:Name="Lossless" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.Lossy.Value, Mode=OneWay}" x:Name="Lossy" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.None.Value, Mode=OneWay}" x:Name="None" />
                     </ComboBox>
                     </ComboBox>
                 </StackPanel>
                 </StackPanel>
 
 
@@ -127,7 +129,7 @@
                         Classes="txt"
                         Classes="txt"
                         IsEnabled="{Binding Path=IsChecked, ElementName=IsQualityEnabledBox}"
                         IsEnabled="{Binding Path=IsChecked, ElementName=IsQualityEnabledBox}"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Quality,
+                        Text="{CompiledBinding Translation.Quality.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         Width="130" />
                         Width="130" />
 
 
@@ -157,7 +159,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Resize,
+                        Text="{CompiledBinding Translation.Resize.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="ResizeTextBlock" />
                         x:Name="ResizeTextBlock" />
@@ -174,11 +176,11 @@
                         SelectedItem="NoResizeBox"
                         SelectedItem="NoResizeBox"
                         Width="195"
                         Width="195"
                         x:Name="ResizeComboBox">
                         x:Name="ResizeComboBox">
-                        <ComboBoxItem Content="{CompiledBinding Translation.NoResize}" x:Name="NoResizeBox" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="WidthResizeBox" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="HeightResizeBox" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.WidthAndHeight}" x:Name="WidthAndHeightResizeBox" />
-                        <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="PercentageResizeBox" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.NoResize.Value, Mode=OneWay}" x:Name="NoResizeBox" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="WidthResizeBox" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="HeightResizeBox" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.WidthAndHeight.Value, Mode=OneWay}" x:Name="WidthAndHeightResizeBox" />
+                        <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="PercentageResizeBox" />
                     </ComboBox>
                     </ComboBox>
                 </StackPanel>
                 </StackPanel>
 
 
@@ -191,14 +193,15 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Width}"
+                        Text="{CompiledBinding Translation.Width.Value,
+                                               Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="WidthTextBlock" />
                         x:Name="WidthTextBlock" />
 
 
                     <customControls:NumTextBox
                     <customControls:NumTextBox
                         Classes="hover TStyle"
                         Classes="hover TStyle"
                         Margin="11,0,10,0"
                         Margin="11,0,10,0"
-                        Text="{CompiledBinding PicViewer.PixelWidth,
+                        Text="{CompiledBinding PicViewer.PixelWidth.Value,
                                                Mode=OneTime}"
                                                Mode=OneTime}"
                         Width="195"
                         Width="195"
                         x:Name="WidthValueBox" />
                         x:Name="WidthValueBox" />
@@ -213,14 +216,15 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Height}"
+                        Text="{CompiledBinding Translation.Height.Value,
+                                               Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="HeightTextBlock" />
                         x:Name="HeightTextBlock" />
 
 
                     <customControls:NumTextBox
                     <customControls:NumTextBox
                         Classes="hover TStyle"
                         Classes="hover TStyle"
                         Margin="11,0,10,0"
                         Margin="11,0,10,0"
-                        Text="{CompiledBinding PicViewer.PixelHeight,
+                        Text="{CompiledBinding PicViewer.PixelHeight.Value,
                                                Mode=OneTime}"
                                                Mode=OneTime}"
                         Width="195"
                         Width="195"
                         x:Name="HeightValueBox" />
                         x:Name="HeightValueBox" />
@@ -234,7 +238,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Width}"
+                        Text="{CompiledBinding Translation.Width.Value,
+                                               Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="WidthAndHeightWidthTextBlock" />
                         x:Name="WidthAndHeightWidthTextBlock" />
 
 
@@ -368,7 +373,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Height}"
+                        Text="{CompiledBinding Translation.Height.Value,
+                                               Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="WidthAndHeightHeightTextBlock" />
                         x:Name="WidthAndHeightHeightTextBlock" />
 
 
@@ -389,7 +395,8 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.Percentage}"
+                        Text="{CompiledBinding Translation.Percentage.Value,
+                                               Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="PercentageTextBlock" />
                         x:Name="PercentageTextBlock" />
 
 
@@ -411,7 +418,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Margin="5,0,0,0"
                         Margin="5,0,0,0"
-                        Text="{CompiledBinding Translation.GenerateThumbnails,
+                        Text="{CompiledBinding Translation.GenerateThumbnails.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         Width="130"
                         Width="130"
                         x:Name="GenerateThumbnailsTextBlock" />
                         x:Name="GenerateThumbnailsTextBlock" />
@@ -454,10 +461,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 1" />
                                 <Run Text=" 1" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -491,9 +498,9 @@
                                 SelectedItem="Thumb1PercentageItem"
                                 SelectedItem="Thumb1PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb1ComboBox">
                                 x:Name="Thumb1ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb1PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb1WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb1HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb1PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb1WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb1HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -520,10 +527,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 2" />
                                 <Run Text=" 2" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -541,7 +548,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -557,9 +565,9 @@
                                 SelectedItem="Thumb2PercentageItem"
                                 SelectedItem="Thumb2PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb2ComboBox">
                                 x:Name="Thumb2ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb2PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb2WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb2HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb2PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb2WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb2HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -586,10 +594,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 3" />
                                 <Run Text=" 3" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -607,7 +615,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -623,9 +632,9 @@
                                 SelectedItem="Thumb3PercentageItem"
                                 SelectedItem="Thumb3PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb3ComboBox">
                                 x:Name="Thumb3ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb3PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb3WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb3HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb3PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb3WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb3HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -652,10 +661,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 4" />
                                 <Run Text=" 4" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -673,7 +682,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -689,9 +699,9 @@
                                 SelectedItem="Thumb4PercentageItem"
                                 SelectedItem="Thumb4PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb4ComboBox">
                                 x:Name="Thumb4ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb4PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb4WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb4HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb4PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb4WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb4HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -717,10 +727,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 5" />
                                 <Run Text=" 5" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -738,7 +748,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -754,9 +765,9 @@
                                 SelectedItem="Thumb5PercentageItem"
                                 SelectedItem="Thumb5PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb5ComboBox">
                                 x:Name="Thumb5ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb5PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb5WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb5HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb5PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb5WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb5HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -782,10 +793,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 6" />
                                 <Run Text=" 6" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -803,7 +814,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -819,9 +831,9 @@
                                 SelectedItem="Thumb6PercentageItem"
                                 SelectedItem="Thumb6PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb6ComboBox">
                                 x:Name="Thumb6ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb6PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb61WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb6HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb6PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb61WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb6HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -848,10 +860,10 @@
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
                                 Width="130">
                                 Width="130">
-                                <Run Text="{CompiledBinding Translation.Thumbnail}" />
+                                <Run Text="{CompiledBinding Translation.Thumbnail.Value, Mode=OneWay}" />
                                 <Run Text=" 7" />
                                 <Run Text=" 7" />
                                 <LineBreak />
                                 <LineBreak />
-                                <Run Text="{CompiledBinding Translation.OutputFolder}" />
+                                <Run Text="{CompiledBinding Translation.OutputFolder.Value, Mode=OneWay}" />
                             </TextBlock>
                             </TextBlock>
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
                                 Classes="hover TStyle"
                                 Classes="hover TStyle"
@@ -869,7 +881,8 @@
                             <TextBlock
                             <TextBlock
                                 Classes="txt"
                                 Classes="txt"
                                 Margin="8,0,0,0"
                                 Margin="8,0,0,0"
-                                Text="{CompiledBinding Translation.Resize}"
+                                Text="{CompiledBinding Translation.Resize.Value,
+                                                       Mode=OneWay}"
                                 Width="125" />
                                 Width="125" />
 
 
                             <ComboBox
                             <ComboBox
@@ -885,9 +898,9 @@
                                 SelectedItem="Thumb7PercentageItem"
                                 SelectedItem="Thumb7PercentageItem"
                                 Width="195"
                                 Width="195"
                                 x:Name="Thumb7ComboBox">
                                 x:Name="Thumb7ComboBox">
-                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage}" x:Name="Thumb7PercentageItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Width}" x:Name="Thumb7WidthItem" />
-                                <ComboBoxItem Content="{CompiledBinding Translation.Height}" x:Name="Thumb7HeightItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Percentage.Value, Mode=OneWay}" x:Name="Thumb7PercentageItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Width.Value, Mode=OneWay}" x:Name="Thumb7WidthItem" />
+                                <ComboBoxItem Content="{CompiledBinding Translation.Height.Value, Mode=OneWay}" x:Name="Thumb7HeightItem" />
                             </ComboBox>
                             </ComboBox>
 
 
                             <customControls:FuncTextBox
                             <customControls:FuncTextBox
@@ -945,7 +958,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Padding="15,0"
                         Padding="15,0"
-                        Text="{CompiledBinding Translation.Cancel,
+                        Text="{CompiledBinding Translation.Cancel.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         TextAlignment="Center"
                         TextAlignment="Center"
                         x:Name="CancelButtonTextBlock" />
                         x:Name="CancelButtonTextBlock" />
@@ -959,7 +972,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Padding="15,0"
                         Padding="15,0"
-                        Text="{CompiledBinding Translation.Reset,
+                        Text="{CompiledBinding Translation.Reset.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         TextAlignment="Center"
                         TextAlignment="Center"
                         x:Name="ResetButtonTextBlock" />
                         x:Name="ResetButtonTextBlock" />
@@ -974,7 +987,7 @@
                     <TextBlock
                     <TextBlock
                         Classes="txt"
                         Classes="txt"
                         Padding="15,0"
                         Padding="15,0"
-                        Text="{CompiledBinding Translation.Start,
+                        Text="{CompiledBinding Translation.Start.Value,
                                                Mode=OneWay}"
                                                Mode=OneWay}"
                         TextAlignment="Center" />
                         TextAlignment="Center" />
                 </Button>
                 </Button>

Some files were not shown because too many files changed in this diff