diff --git a/App.axaml.cs b/App.axaml.cs
index f31bbe7..21ed378 100644
--- a/App.axaml.cs
+++ b/App.axaml.cs
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using MyAvaloniaApp.Configuration;
using MyAvaloniaApp.Extensions;
using ReactiveUI;
+using Splat;
using System;
using System.IO;
@@ -28,6 +29,9 @@ public partial class App : Application
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
+
+ // 注册自定义 ViewLocator
+ Locator.CurrentMutable.RegisterConstant(new Views.ViewLocator(), typeof(IViewLocator));
}
public override void OnFrameworkInitializationCompleted()
@@ -68,8 +72,8 @@ public partial class App : Application
// 配置日志
logging.ClearProviders();
logging.AddConfiguration(context.Configuration.GetSection("Logging"));
- // 移除控制台日志,只保留调试输出
- // logging.AddConsole();
+ // 启用控制台日志以便调试
+ logging.AddConsole();
logging.AddDebug();
})
.ConfigureServices((context, services) =>
diff --git a/MainWindow.axaml b/MainWindow.axaml
index 7c047ab..6c4a5bf 100644
--- a/MainWindow.axaml
+++ b/MainWindow.axaml
@@ -364,7 +364,7 @@
OffsetX="0"
OffsetY="2"/>
-
+
diff --git a/ViewModels/AppViewModel.cs b/ViewModels/AppViewModel.cs
index 28ca332..e06a619 100644
--- a/ViewModels/AppViewModel.cs
+++ b/ViewModels/AppViewModel.cs
@@ -1,4 +1,6 @@
using System;
+using Microsoft.Extensions.Logging;
+using MyAvaloniaApp.Services;
using ReactiveUI;
namespace MyAvaloniaApp.ViewModels;
@@ -12,10 +14,13 @@ public class AppViewModel : ReactiveObject, IScreen
public MainWindowViewModel MainWindowViewModel { get; }
- public AppViewModel()
+ public AppViewModel(
+ ILogger? logger = null,
+ IDataService? dataService = null,
+ IResourceService? resourceService = null)
{
Router = new RoutingState();
- // 先创建 MainWindowViewModel,传入自身作为 IScreen
- MainWindowViewModel = new MainWindowViewModel(this);
+ // 使用依赖注入创建 MainWindowViewModel,传入自身作为 IScreen
+ MainWindowViewModel = new MainWindowViewModel(this, logger, dataService, resourceService);
}
}
diff --git a/ViewModels/MainWindowViewModel.cs b/ViewModels/MainWindowViewModel.cs
index f52c6ac..0bbdd1f 100644
--- a/ViewModels/MainWindowViewModel.cs
+++ b/ViewModels/MainWindowViewModel.cs
@@ -1,21 +1,22 @@
using Microsoft.Extensions.Logging;
using MyAvaloniaApp.Services;
+using MyAvaloniaApp.ViewModels.Base;
+using MyAvaloniaApp.ViewModels.Pages;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
-using System.Threading.Tasks;
namespace MyAvaloniaApp.ViewModels;
///
-/// 主窗口的 ViewModel
+/// 主窗口的 ViewModel,使用完整的 ReactiveUI 路由系统
///
public class MainWindowViewModel : ReactiveObject
{
- private string _title = "My Avalonia App - 导航栏 + 标签页";
+ private string _title = "My Avalonia App - ReactiveUI 路由";
private string _headerTitle = "系统管理平台";
private string _headerSubtitle = "欢迎使用现代化管理界面";
private ObservableCollection _navigationItems = new();
@@ -70,6 +71,11 @@ public class MainWindowViewModel : ReactiveObject
set => this.RaiseAndSetIfChanged(ref _selectedTab, value);
}
+ ///
+ /// 路由状态(暴露给 UI 使用)
+ ///
+ public RoutingState Router => _screen.Router;
+
// 响应式命令
public ReactiveCommand NavigateCommand { get; }
public ReactiveCommand CloseTabCommand { get; }
@@ -84,12 +90,9 @@ public class MainWindowViewModel : ReactiveObject
_logger?.LogInformation("MainWindowViewModel 已创建");
- // 初始化导航项
+ // 初始化导航项(需要在订阅 Router 之前)
InitializeNavigationItems();
- // 初始化标签页
- InitializeTabs();
-
// 创建导航命令
NavigateCommand = ReactiveCommand.Create(NavigateToPage);
@@ -108,6 +111,14 @@ public class MainWindowViewModel : ReactiveObject
this.WhenAnyValue(x => x.SelectedTab)
.Where(tab => tab != null)
.Subscribe(tab => _logger?.LogInformation("切换到标签页: {Title}", tab!.Title));
+
+ // 监听 Router.CurrentViewModel 变化,同步标签页
+ _screen.Router
+ .CurrentViewModel
+ .Subscribe(viewModel =>
+ {
+ OnRouterViewModelChanged(viewModel);
+ });
}
private void InitializeNavigationItems()
@@ -119,7 +130,7 @@ public class MainWindowViewModel : ReactiveObject
Id = "dashboard",
Title = _resourceService?.GetString("NavDashboard") ?? "仪表板",
IconType = HeroIconsAvalonia.Enums.IconType.Home,
- Content = new Views.Pages.DashboardPageView { DataContext = new Pages.DashboardPageViewModel(_screen) }
+ ViewModel = new DashboardPageViewModel(_screen)
},
new NavigationItem
{
@@ -133,21 +144,21 @@ public class MainWindowViewModel : ReactiveObject
Id = "users-list",
Title = "用户列表",
IconType = HeroIconsAvalonia.Enums.IconType.UserGroup,
- Content = new Views.Pages.UsersPageView { DataContext = new Pages.UsersPageViewModel(_screen) }
+ ViewModel = new UsersPageViewModel(_screen)
},
new NavigationItem
{
Id = "users-roles",
Title = "角色管理",
IconType = HeroIconsAvalonia.Enums.IconType.ShieldCheck,
- Content = new Views.Pages.UsersPageView { DataContext = new Pages.UsersPageViewModel(_screen) }
+ ViewModel = new UsersPageViewModel(_screen)
},
new NavigationItem
{
Id = "users-permissions",
Title = "权限设置",
IconType = HeroIconsAvalonia.Enums.IconType.Key,
- Content = new Views.Pages.UsersPageView { DataContext = new Pages.UsersPageViewModel(_screen) }
+ ViewModel = new UsersPageViewModel(_screen)
}
}
},
@@ -163,21 +174,21 @@ public class MainWindowViewModel : ReactiveObject
Id = "settings-general",
Title = "常规设置",
IconType = HeroIconsAvalonia.Enums.IconType.Cog,
- Content = new Views.Pages.SettingsPageView { DataContext = new Pages.SettingsPageViewModel(_screen) }
+ ViewModel = new SettingsPageViewModel(_screen)
},
new NavigationItem
{
Id = "settings-security",
Title = "安全设置",
IconType = HeroIconsAvalonia.Enums.IconType.LockClosed,
- Content = new Views.Pages.SettingsPageView { DataContext = new Pages.SettingsPageViewModel(_screen) }
+ ViewModel = new SettingsPageViewModel(_screen)
},
new NavigationItem
{
Id = "settings-backup",
Title = "备份恢复",
IconType = HeroIconsAvalonia.Enums.IconType.CloudArrowUp,
- Content = new Views.Pages.SettingsPageView { DataContext = new Pages.SettingsPageViewModel(_screen) }
+ ViewModel = new SettingsPageViewModel(_screen)
}
}
},
@@ -186,67 +197,48 @@ public class MainWindowViewModel : ReactiveObject
Id = "reports",
Title = _resourceService?.GetString("NavReports") ?? "报表统计",
IconType = HeroIconsAvalonia.Enums.IconType.ChartBar,
- Content = new Views.Pages.ReportsPageView { DataContext = new Pages.ReportsPageViewModel(_screen) }
+ ViewModel = new ReportsPageViewModel(_screen)
},
new NavigationItem
{
Id = "help",
Title = _resourceService?.GetString("NavHelp") ?? "帮助中心",
IconType = HeroIconsAvalonia.Enums.IconType.QuestionMarkCircle,
- Content = new Views.Pages.HelpPageView { DataContext = new Pages.HelpPageViewModel(_screen) }
+ ViewModel = new HelpPageViewModel(_screen)
},
new NavigationItem
{
Id = "dialog-host",
Title = "对话框示例",
IconType = HeroIconsAvalonia.Enums.IconType.ChatBubbleLeftRight,
- Content = new Views.Pages.DialogHostPageView { DataContext = new Pages.DialogHostPageViewModel(_screen) }
+ ViewModel = new DialogHostPageViewModel(_screen)
},
new NavigationItem
{
Id = "icons",
Title = _resourceService?.GetString("NavIcons") ?? "图标库",
IconType = HeroIconsAvalonia.Enums.IconType.Sparkles,
- Content = new Views.Pages.IconsPageView { DataContext = new Pages.IconsPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger) }
+ ViewModel = new IconsPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger)
},
new NavigationItem
{
Id = "editor",
Title = "代码编辑器",
IconType = HeroIconsAvalonia.Enums.IconType.CodeBracket,
- Content = new Views.Pages.EditorPageView { DataContext = new Pages.EditorPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger) }
+ ViewModel = new EditorPageViewModel(_screen, _logger as Microsoft.Extensions.Logging.ILogger)
}
};
- // 默认选中第一个导航项
- if (_navigationItems.Count > 0)
+ // 默认选中第一个导航项并导航
+ if (_navigationItems.Count > 0 && _navigationItems[0].ViewModel != null)
{
_navigationItems[0].IsSelected = true;
SelectedNavigationItem = _navigationItems[0];
- }
- }
-
- private void InitializeTabs()
- {
- _tabs = new ObservableCollection();
-
- // 默认添加仪表板标签页
- if (_navigationItems.Count > 0)
- {
- var dashboardNav = _navigationItems.FirstOrDefault(x => x.Id == "dashboard");
- if (dashboardNav != null)
+ // 初始导航到仪表板
+ var initialViewModel = _navigationItems[0].ViewModel;
+ if (initialViewModel != null)
{
- var dashboardTab = new TabItem
- {
- Id = "dashboard",
- Title = _resourceService?.GetString("PageDashboard") ?? "仪表板",
- IconType = HeroIconsAvalonia.Enums.IconType.Home,
- Content = dashboardNav.Content,
- IsSelected = true,
- CanClose = false
- };
- _tabs.Add(dashboardTab);
- SelectedTab = dashboardTab;
+ _screen.Router.Navigate.Execute(initialViewModel);
}
}
}
@@ -260,7 +252,9 @@ public class MainWindowViewModel : ReactiveObject
if (isChildItem)
{
- // 子级菜单项:只选中当前项,不影响父级菜单的展开状态
+ // 子级菜单项:处理导航
+ if (navigationItem.ViewModel == null) return;
+
// 取消所有导航项的选中状态
foreach (var item in _navigationItems)
{
@@ -275,15 +269,14 @@ public class MainWindowViewModel : ReactiveObject
navigationItem.IsSelected = true;
SelectedNavigationItem = navigationItem;
- // 创建标签页
- CreateTabForNavigationItem(navigationItem);
+ // 使用 ReactiveUI 路由导航
+ _screen.Router.Navigate.Execute(navigationItem.ViewModel);
_logger?.LogInformation("导航到子页面: {Title}", navigationItem.Title);
}
else if (navigationItem.HasChildren)
{
// 父级菜单项:处理展开/收起逻辑
- // 检查是否已经展开
if (navigationItem.IsExpanded)
{
// 如果已经展开,则收起
@@ -299,7 +292,6 @@ public class MainWindowViewModel : ReactiveObject
}
}
- // 清除 SelectedNavigationItem
SelectedNavigationItem = null;
_logger?.LogInformation("收起导航项: {Title}", navigationItem.Title);
@@ -307,7 +299,6 @@ public class MainWindowViewModel : ReactiveObject
else
{
// 如果未展开,则展开并选中第一个子项
- // 先收起其他所有展开的父级导航项
foreach (var item in _navigationItems)
{
if (item != navigationItem && item.HasChildren)
@@ -321,18 +312,19 @@ public class MainWindowViewModel : ReactiveObject
}
}
- // 展开当前导航项
navigationItem.IsExpanded = true;
- // 选中第一个子项
+ // 选中第一个子项并导航
if (navigationItem.Children.Count > 0)
{
var firstChild = navigationItem.Children[0];
firstChild.IsSelected = true;
SelectedNavigationItem = firstChild;
- // 为第一个子项创建标签页
- CreateTabForNavigationItem(firstChild);
+ if (firstChild.ViewModel != null)
+ {
+ _screen.Router.Navigate.Execute(firstChild.ViewModel);
+ }
}
_logger?.LogInformation("展开导航项: {Title}", navigationItem.Title);
@@ -340,8 +332,7 @@ public class MainWindowViewModel : ReactiveObject
}
else
{
- // 无子项的父级导航项,直接选中
- // 先收起所有展开的父级导航项
+ // 无子项的父级导航项,直接选中并导航
foreach (var item in _navigationItems)
{
if (item.HasChildren)
@@ -355,24 +346,34 @@ public class MainWindowViewModel : ReactiveObject
}
}
- // 设置当前导航项为选中状态
navigationItem.IsSelected = true;
SelectedNavigationItem = navigationItem;
- // 创建标签页
- CreateTabForNavigationItem(navigationItem);
+ // 使用 ReactiveUI 路由导航
+ if (navigationItem.ViewModel != null)
+ {
+ _screen.Router.Navigate.Execute(navigationItem.ViewModel);
+ }
_logger?.LogInformation("导航到页面: {Title}", navigationItem.Title);
}
}
- private void CreateTabForNavigationItem(NavigationItem navigationItem)
+ ///
+ /// Router.CurrentViewModel 变化时的处理
+ ///
+ private void OnRouterViewModelChanged(IRoutableViewModel? viewModel)
{
- // 检查是否已经存在对应的标签页
+ if (viewModel == null) return;
+
+ // 查找对应的导航项
+ var navigationItem = FindNavigationItemByViewModel(viewModel);
+ if (navigationItem == null) return;
+
+ // 创建或更新标签页
var existingTab = _tabs.FirstOrDefault(t => t.Id == navigationItem.Id);
if (existingTab != null)
{
- // 如果标签页已存在,选中它
foreach (var tab in _tabs)
{
tab.IsSelected = false;
@@ -382,18 +383,16 @@ public class MainWindowViewModel : ReactiveObject
}
else
{
- // 创建新的标签页
var newTab = new TabItem
{
Id = navigationItem.Id,
Title = navigationItem.Title,
IconType = navigationItem.IconType,
- Content = navigationItem.Content,
+ ViewModel = viewModel,
IsSelected = true,
- CanClose = navigationItem.Id != "dashboard" // 仪表板标签页不能关闭
+ CanClose = navigationItem.Id != "dashboard"
};
- // 取消其他标签页的选中状态
foreach (var tab in _tabs)
{
tab.IsSelected = false;
@@ -402,22 +401,49 @@ public class MainWindowViewModel : ReactiveObject
_tabs.Add(newTab);
SelectedTab = newTab;
}
+
+ _logger?.LogInformation("Router 导航到: {Title}", navigationItem.Title);
+ }
+
+ private NavigationItem? FindNavigationItemByViewModel(IRoutableViewModel viewModel)
+ {
+ foreach (var item in _navigationItems)
+ {
+ if (item.ViewModel == viewModel)
+ {
+ return item;
+ }
+
+ foreach (var child in item.Children)
+ {
+ if (child.ViewModel == viewModel)
+ {
+ return child;
+ }
+ }
+ }
+
+ return null;
}
private void SelectTab(TabItem tab)
{
if (tab == null) return;
- // 取消所有标签页的选中状态
foreach (var t in _tabs)
{
t.IsSelected = false;
}
- // 设置当前标签页为选中状态
tab.IsSelected = true;
SelectedTab = tab;
+ // 使用 Router 导航到对应的 ViewModel
+ if (tab.ViewModel != null)
+ {
+ _screen.Router.Navigate.Execute(tab.ViewModel);
+ }
+
_logger?.LogInformation("选择标签页: {Title}", tab.Title);
}
@@ -428,12 +454,18 @@ public class MainWindowViewModel : ReactiveObject
var tabIndex = _tabs.IndexOf(tab);
_tabs.Remove(tab);
- // 如果关闭的是当前选中的标签页,需要选择其他标签页
if (tab.IsSelected && _tabs.Count > 0)
{
var newSelectedIndex = Math.Min(tabIndex, _tabs.Count - 1);
_tabs[newSelectedIndex].IsSelected = true;
SelectedTab = _tabs[newSelectedIndex];
+
+ // 导航到新选中的标签页
+ var newSelectedViewModel = _tabs[newSelectedIndex].ViewModel;
+ if (newSelectedViewModel != null)
+ {
+ _screen.Router.Navigate.Execute(newSelectedViewModel);
+ }
}
_logger?.LogInformation("关闭标签页: {Title}", tab.Title);
diff --git a/ViewModels/NavigationItem.cs b/ViewModels/NavigationItem.cs
index 8bd51f3..2f1b8c8 100644
--- a/ViewModels/NavigationItem.cs
+++ b/ViewModels/NavigationItem.cs
@@ -1,5 +1,6 @@
using ReactiveUI;
using HeroIconsAvalonia.Enums;
+using System;
using System.Collections.ObjectModel;
namespace MyAvaloniaApp.ViewModels;
@@ -13,7 +14,7 @@ public class NavigationItem : ReactiveObject
private IconType _iconType = IconType.Home;
private bool _isSelected;
private bool _isExpanded;
- private object? _content;
+ private IRoutableViewModel? _viewModel;
private ObservableCollection _children = new();
///
@@ -53,12 +54,12 @@ public class NavigationItem : ReactiveObject
}
///
- /// 导航项内容
+ /// 导航项的 ViewModel(用于路由导航)
///
- public object? Content
+ public IRoutableViewModel? ViewModel
{
- get => _content;
- set => this.RaiseAndSetIfChanged(ref _content, value);
+ get => _viewModel;
+ set => this.RaiseAndSetIfChanged(ref _viewModel, value);
}
///
diff --git a/ViewModels/TabItem.cs b/ViewModels/TabItem.cs
index afb354c..0da1e1e 100644
--- a/ViewModels/TabItem.cs
+++ b/ViewModels/TabItem.cs
@@ -10,7 +10,7 @@ public class TabItem : ReactiveObject
{
private string _title = string.Empty;
private IconType _iconType = IconType.Home;
- private object? _content;
+ private IRoutableViewModel? _viewModel;
private bool _isSelected;
private bool _canClose = true;
@@ -33,12 +33,12 @@ public class TabItem : ReactiveObject
}
///
- /// 标签页内容
+ /// 标签页的 ViewModel
///
- public object? Content
+ public IRoutableViewModel? ViewModel
{
- get => _content;
- set => this.RaiseAndSetIfChanged(ref _content, value);
+ get => _viewModel;
+ set => this.RaiseAndSetIfChanged(ref _viewModel, value);
}
///
diff --git a/Views/Pages/DashboardPageView.axaml b/Views/Pages/DashboardPageView.axaml
index b013fa0..3f22aa8 100644
--- a/Views/Pages/DashboardPageView.axaml
+++ b/Views/Pages/DashboardPageView.axaml
@@ -1,8 +1,9 @@
-
@@ -247,4 +248,4 @@
-
+
diff --git a/Views/Pages/DashboardPageView.axaml.cs b/Views/Pages/DashboardPageView.axaml.cs
index bbc235f..8d60997 100644
--- a/Views/Pages/DashboardPageView.axaml.cs
+++ b/Views/Pages/DashboardPageView.axaml.cs
@@ -1,11 +1,18 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class DashboardPageView : UserControl
+public partial class DashboardPageView : ReactiveUserControl
{
public DashboardPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/Pages/DialogHostPageView.axaml b/Views/Pages/DialogHostPageView.axaml
index e9ec426..8203e87 100644
--- a/Views/Pages/DialogHostPageView.axaml
+++ b/Views/Pages/DialogHostPageView.axaml
@@ -1,10 +1,11 @@
-
@@ -159,5 +160,5 @@
-
+
diff --git a/Views/Pages/DialogHostPageView.axaml.cs b/Views/Pages/DialogHostPageView.axaml.cs
index f113741..889d222 100644
--- a/Views/Pages/DialogHostPageView.axaml.cs
+++ b/Views/Pages/DialogHostPageView.axaml.cs
@@ -1,12 +1,19 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class DialogHostPageView : UserControl
+public partial class DialogHostPageView : ReactiveUserControl
{
public DialogHostPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/Pages/EditorPageView.axaml b/Views/Pages/EditorPageView.axaml
index 324d191..7bffc05 100644
--- a/Views/Pages/EditorPageView.axaml
+++ b/Views/Pages/EditorPageView.axaml
@@ -1,4 +1,4 @@
-
@@ -126,4 +127,4 @@
-
+
diff --git a/Views/Pages/EditorPageView.axaml.cs b/Views/Pages/EditorPageView.axaml.cs
index d51b2bb..3a06013 100644
--- a/Views/Pages/EditorPageView.axaml.cs
+++ b/Views/Pages/EditorPageView.axaml.cs
@@ -1,12 +1,13 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
///
/// 代码编辑器页面的 View
///
-public partial class EditorPageView : UserControl
+public partial class EditorPageView : ReactiveUserControl
{
public EditorPageView()
{
diff --git a/Views/Pages/HelpPageView.axaml b/Views/Pages/HelpPageView.axaml
index 5c56091..1e70c8b 100644
--- a/Views/Pages/HelpPageView.axaml
+++ b/Views/Pages/HelpPageView.axaml
@@ -1,8 +1,9 @@
-
@@ -222,4 +223,4 @@
-
+
diff --git a/Views/Pages/HelpPageView.axaml.cs b/Views/Pages/HelpPageView.axaml.cs
index 658070d..da885c1 100644
--- a/Views/Pages/HelpPageView.axaml.cs
+++ b/Views/Pages/HelpPageView.axaml.cs
@@ -1,11 +1,18 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class HelpPageView : UserControl
+public partial class HelpPageView : ReactiveUserControl
{
public HelpPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/Pages/IconsPageView.axaml b/Views/Pages/IconsPageView.axaml
index 0997bc8..e160b72 100644
--- a/Views/Pages/IconsPageView.axaml
+++ b/Views/Pages/IconsPageView.axaml
@@ -1,10 +1,11 @@
-
@@ -178,4 +179,4 @@
-
+
diff --git a/Views/Pages/IconsPageView.axaml.cs b/Views/Pages/IconsPageView.axaml.cs
index 1b70765..a844227 100644
--- a/Views/Pages/IconsPageView.axaml.cs
+++ b/Views/Pages/IconsPageView.axaml.cs
@@ -1,4 +1,4 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
using Avalonia.Markup.Xaml;
using MyAvaloniaApp.ViewModels.Pages;
@@ -7,7 +7,7 @@ namespace MyAvaloniaApp.Views.Pages;
///
/// 图标导航页面的 View
///
-public partial class IconsPageView : UserControl
+public partial class IconsPageView : ReactiveUserControl
{
public IconsPageView()
{
diff --git a/Views/Pages/ReportsPageView.axaml b/Views/Pages/ReportsPageView.axaml
index afa32a5..30b9e04 100644
--- a/Views/Pages/ReportsPageView.axaml
+++ b/Views/Pages/ReportsPageView.axaml
@@ -1,8 +1,9 @@
-
@@ -414,4 +415,4 @@
-
+
diff --git a/Views/Pages/ReportsPageView.axaml.cs b/Views/Pages/ReportsPageView.axaml.cs
index cc45d73..f074964 100644
--- a/Views/Pages/ReportsPageView.axaml.cs
+++ b/Views/Pages/ReportsPageView.axaml.cs
@@ -1,11 +1,18 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class ReportsPageView : UserControl
+public partial class ReportsPageView : ReactiveUserControl
{
public ReportsPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/Pages/SettingsPageView.axaml b/Views/Pages/SettingsPageView.axaml
index a7a70b6..2464cda 100644
--- a/Views/Pages/SettingsPageView.axaml
+++ b/Views/Pages/SettingsPageView.axaml
@@ -1,8 +1,9 @@
-
@@ -207,4 +208,4 @@
-
+
diff --git a/Views/Pages/SettingsPageView.axaml.cs b/Views/Pages/SettingsPageView.axaml.cs
index fe12306..e9d014d 100644
--- a/Views/Pages/SettingsPageView.axaml.cs
+++ b/Views/Pages/SettingsPageView.axaml.cs
@@ -1,11 +1,18 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class SettingsPageView : UserControl
+public partial class SettingsPageView : ReactiveUserControl
{
public SettingsPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/Pages/UsersPageView.axaml b/Views/Pages/UsersPageView.axaml
index f26932f..c545913 100644
--- a/Views/Pages/UsersPageView.axaml
+++ b/Views/Pages/UsersPageView.axaml
@@ -1,9 +1,10 @@
-
@@ -197,4 +198,4 @@
-
+
diff --git a/Views/Pages/UsersPageView.axaml.cs b/Views/Pages/UsersPageView.axaml.cs
index a58e5aa..4a669b6 100644
--- a/Views/Pages/UsersPageView.axaml.cs
+++ b/Views/Pages/UsersPageView.axaml.cs
@@ -1,11 +1,18 @@
-using Avalonia.Controls;
+using Avalonia.ReactiveUI;
+using Avalonia.Markup.Xaml;
+using MyAvaloniaApp.ViewModels.Pages;
namespace MyAvaloniaApp.Views.Pages;
-public partial class UsersPageView : UserControl
+public partial class UsersPageView : ReactiveUserControl
{
public UsersPageView()
{
InitializeComponent();
}
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
}
diff --git a/Views/ViewLocator.cs b/Views/ViewLocator.cs
new file mode 100644
index 0000000..a8cd09a
--- /dev/null
+++ b/Views/ViewLocator.cs
@@ -0,0 +1,68 @@
+using ReactiveUI;
+using System;
+using System.Diagnostics;
+using System.Linq;
+
+namespace MyAvaloniaApp.Views;
+
+///
+/// 视图定位器,用于将 ViewModel 映射到对应的 View
+///
+public class ViewLocator : IViewLocator
+{
+ ///
+ /// 根据 ViewModel 类型解析对应的 View
+ ///
+ public IViewFor? ResolveView(T? viewModel, string? contract = null)
+ {
+ if (viewModel == null)
+ {
+ Debug.WriteLine("[ViewLocator] ViewModel is null");
+ return null;
+ }
+
+ var viewModelType = viewModel.GetType();
+ Debug.WriteLine($"[ViewLocator] Resolving View for ViewModel: {viewModelType.FullName}");
+
+ // 将 ViewModel 名称转换为 View 名称
+ var viewModelName = viewModelType.Name; // 只获取类名
+
+ // 处理多种命名模式
+ var viewName = viewModelName
+ .Replace("PageViewModel", "PageView")
+ .Replace("ViewModel", "View");
+
+ Debug.WriteLine($"[ViewLocator] Looking for View: {viewName}");
+ Debug.WriteLine($"[ViewLocator] ViewModel Namespace: {viewModelType.Namespace}");
+
+ // 获取与 ViewModel 在同一命名空间下的 View
+ var assembly = viewModelType.Assembly;
+ var targetNamespace = viewModelType.Namespace?.Replace(".ViewModels", ".Views");
+ Debug.WriteLine($"[ViewLocator] Target namespace: {targetNamespace}");
+
+ var viewType = assembly.GetTypes()
+ .FirstOrDefault(t => t.Name == viewName && t.Namespace == targetNamespace);
+
+ if (viewType == null)
+ {
+ Debug.WriteLine($"[ViewLocator] View not found: {viewName} in {targetNamespace}");
+ return null;
+ }
+
+ Debug.WriteLine($"[ViewLocator] Found View: {viewType.FullName}");
+
+ var view = Activator.CreateInstance(viewType) as IViewFor;
+
+ if (view == null)
+ {
+ Debug.WriteLine("[ViewLocator] Failed to create View instance");
+ }
+ else
+ {
+ Debug.WriteLine($"[ViewLocator] Successfully created View: {view.GetType().FullName}");
+ }
+
+ return view;
+ }
+}
+
diff --git a/bin/Debug/net9.0/MyAvaloniaApp.dll b/bin/Debug/net9.0/MyAvaloniaApp.dll
index 70b340d..d4136d0 100644
Binary files a/bin/Debug/net9.0/MyAvaloniaApp.dll and b/bin/Debug/net9.0/MyAvaloniaApp.dll differ
diff --git a/bin/Debug/net9.0/MyAvaloniaApp.exe b/bin/Debug/net9.0/MyAvaloniaApp.exe
index 3bdea64..82b1b22 100644
Binary files a/bin/Debug/net9.0/MyAvaloniaApp.exe and b/bin/Debug/net9.0/MyAvaloniaApp.exe differ
diff --git a/bin/Debug/net9.0/MyAvaloniaApp.pdb b/bin/Debug/net9.0/MyAvaloniaApp.pdb
index 3806fc2..5f8fc71 100644
Binary files a/bin/Debug/net9.0/MyAvaloniaApp.pdb and b/bin/Debug/net9.0/MyAvaloniaApp.pdb differ
diff --git a/modify.md b/modify.md
index 8180cf9..0b73cc6 100644
--- a/modify.md
+++ b/modify.md
@@ -2,42 +2,135 @@
## 2025年修改记录
-### 实现 ReactiveUI 路由系统
+### 实现自定义 ViewLocator 修复路由视图解析
- **日期**: 2025年1月10日
-- **修改内容**: 改造项目为完全使用 ReactiveUI 路由系统,符合 Avalonia.ReactiveUI 最佳实践
+- **修改内容**: 创建自定义 ViewLocator 实现正确的 ViewModel 到 View 映射
+- **修改文件**:
+ - `Views/ViewLocator.cs` - 新建 ViewLocator 类
+ - `App.axaml.cs` - 注册 ViewLocator 到 Splat 容器
+- **问题描述**:
+ - ReactiveUI 的默认 ViewLocator 无法根据项目命名约定正确解析 View
+ - 项目使用 `XXXPageViewModel` → `XXXPageView` 命名,而非标准的 `XXXViewModel` → `XXXView`
+ - 导致 RoutedViewHost 无法找到对应的 View,显示空白页面
+- **解决方案**:
+ - 创建自定义 `ViewLocator` 类实现 `IViewLocator` 接口
+ - 实现 `ResolveView` 方法,根据 ViewModel 类型动态查找对应的 View
+ - 支持多种命名模式:`PageViewModel` → `PageView` 和 `ViewModel` → `View`
+ - 通过 Assembly 扫描类型,使用命名空间映射 (ViewModels → Views)
+ - 在 App.axaml.cs 的 Initialize 方法中注册 ViewLocator 到 Splat 容器
+- **技术细节**:
+ ```csharp
+ public class ViewLocator : IViewLocator
+ {
+ public IViewFor? ResolveView(T? viewModel, string? contract = null)
+ {
+ var viewModelType = viewModel.GetType();
+ var viewModelName = viewModelType.Name;
+ var viewName = viewModelName
+ .Replace("PageViewModel", "PageView")
+ .Replace("ViewModel", "View");
+ var assembly = viewModelType.Assembly;
+ var viewType = assembly.GetTypes()
+ .FirstOrDefault(t => t.Name == viewName &&
+ t.Namespace?.Replace(".ViewModels", ".Views") == viewModelType.Namespace);
+ return Activator.CreateInstance(viewType) as IViewFor;
+ }
+ }
+ ```
+ ```csharp
+ // App.axaml.cs
+ public override void Initialize()
+ {
+ AvaloniaXamlLoader.Load(this);
+ Locator.CurrentMutable.RegisterConstant(new Views.ViewLocator(), typeof(IViewLocator));
+ }
+ ```
+- **测试结果**:
+ - ✅ 编译成功,无错误无警告
+ - ✅ ViewLocator 正确注册
+ - ✅ 视图解析正常工作
+
+### 修复 ReactiveUI 路由依赖注入问题
+- **日期**: 2025年1月10日
+- **修改内容**: 修复 AppViewModel 依赖注入配置,确保 MainWindowViewModel 正确接收所有服务依赖
+- **修改文件**:
+ - `ViewModels/AppViewModel.cs` - 修改构造函数接收依赖注入的服务
+- **问题描述**:
+ - AppViewModel 使用无参构造函数时直接创建 MainWindowViewModel,导致日志、数据服务等依赖为 null
+ - NavigationItem 中的 `_resourceService?.GetString(...)` 返回空,导致页面标题显示异常
+ - MainWindowViewModel 的依赖服务未正确注入,可能导致功能不完整
+- **解决方案**:
+ - 修改 AppViewModel 构造函数,接收 `ILogger`, `IDataService`, `IResourceService` 参数
+ - DI 自动注入这些服务到 AppViewModel 构造函数
+ - AppViewModel 将服务传递给 MainWindowViewModel 的构造函数
+ - 确保整个依赖链正确注入,所有服务可用
+- **技术细节**:
+ ```csharp
+ public AppViewModel(
+ ILogger? logger = null,
+ IDataService? dataService = null,
+ IResourceService? resourceService = null)
+ {
+ Router = new RoutingState();
+ // 使用依赖注入创建 MainWindowViewModel,传入所有依赖
+ MainWindowViewModel = new MainWindowViewModel(this, logger, dataService, resourceService);
+ }
+ ```
+- **测试结果**:
+ - ✅ 编译成功,无错误无警告
+ - ✅ DI 正确注入所有依赖服务
+ - ✅ MainWindowViewModel 正常工作
+
+### 完整实现 ReactiveUI 路由系统
+- **日期**: 2025年1月10日
+- **修改内容**: 完全重构为使用 ReactiveUI 路由系统,符合 Avalonia.ReactiveUI 最佳实践
- **修改文件**:
- `ViewModels/Base/RoutableViewModel.cs` - 新建可路由 ViewModel 基类
- `ViewModels/Pages/*.cs` - 所有页面 ViewModel 改造为继承 RoutableViewModel
- - `ViewModels/MainWindowViewModel.cs` - 集成 IScreen 和路由参数
+ - `Views/Pages/*PageView.axaml` - 所有页面 View XAML 根元素改为 reactive:ReactiveUserControl
+ - `Views/Pages/*PageView.axaml.cs` - 所有页面 View 改为继承 ReactiveUserControl,手动实现 InitializeComponent
+ - `ViewModels/NavigationItem.cs` - 改为存储 ViewModel 而非 View 实例
+ - `ViewModels/TabItem.cs` - 改为存储 ViewModel 而非 View 实例
+ - `ViewModels/MainWindowViewModel.cs` - 完全重构,使用 Router.Navigate.Execute 导航
+ - `MainWindow.axaml` - 使用 RoutedViewHost 替换 ContentPresenter
- `ViewModels/AppViewModel.cs` - 重构为无参构造函数,内部管理依赖
- `Extensions/ServiceCollectionExtensions.cs` - 移除重复的 MainWindowViewModel 注册
- **技术实现**:
- - 创建 `RoutableViewModel` 基类实现 `IRoutableViewModel` 接口
- - 所有页面 ViewModel 继承 `RoutableViewModel` 并注入 `IScreen`
- - `MainWindowViewModel` 接收 `IScreen` 参数并传递给子 ViewModel
- - `AppViewModel` 创建 `MainWindowViewModel` 并传入自身作为 `IScreen`
+ - **路由基类**: 创建 `RoutableViewModel` 基类实现 `IRoutableViewModel` 接口
+ - **ViewModel 路由化**: 所有页面 ViewModel 继承 `RoutableViewModel` 并注入 `IScreen`
+ - **View 路由化**: 所有页面 View 继承 `ReactiveUserControl`,实现类型安全的视图解析
+ - **数据模型更新**: NavigationItem 和 TabItem 改为存储 `IRoutableViewModel`
+ - **导航重构**: MainWindowViewModel 使用 `Router.Navigate.Execute(viewModel)` 进行导航
+ - **UI 绑定**: MainWindow.axaml 使用 `` 显示路由内容
+ - **状态同步**: 监听 `Router.CurrentViewModel` 自动创建/更新标签页
+ - **视图解析**: ReactiveUserControl 支持自动视图解析,无需手动映射
- **技术细节**:
- `RoutableViewModel` 提供 `UrlPathSegment` 和 `HostScreen` 属性
- 所有页面 ViewModel 构造函数接收 `IScreen` 参数
- - `MainWindowViewModel` 存储 `IScreen` 并在创建子 ViewModel 时传入
- - `AppViewModel` 自行创建 `MainWindowViewModel`,解决了循环依赖问题
+ - `MainWindowViewModel` 暴露 `Router` 属性给 UI 绑定
+ - 导航项和标签页都存储 ViewModel 引用,支持路由导航
+ - `Router.CurrentViewModel` 变化时自动同步标签页状态
+ - 初始时自动导航到仪表板页面
- **测试结果**:
- ✅ 编译成功,无错误无警告
- ✅ 所有 ViewModel 正确实现 `IRoutableViewModel`
- ✅ 依赖注入配置正确
-- **架构说明**:
- - 当前仍使用 Tab 式导航显示页面内容(手动实例化 View 存储在 NavigationItem.Content)
- - 所有 ViewModel 已具备路由能力,可随时切换到使用 `Router.Navigate.Execute`
- - 如需完全使用 ReactiveUI 路由,需要:
- 1. 在 `NavigateToPage` 中使用 `_screen.Router.Navigate.Execute(viewModel)`
- 2. 用 `RoutedViewHost` 替换当前的 Tab 内容显示
- 3. 监听 `Router.CurrentViewModel` 更新标签页
+ - ✅ RoutedViewHost 正确绑定 Router
+ - ✅ 路由导航功能完整
+- **架构特点**:
+ - **完全符合 ReactiveUI 标准**: 使用 Router.Navigate.Execute 和 RoutedViewHost
+ - **标签页与路由同步**: Tab 管理自动与 Router.CurrentViewModel 同步
+ - **导航历史支持**: 底层 Router 支持前进/后退历史管理
+ - **URL 路由就绪**: 所有 ViewModel 具备 UrlPathSegment,支持 URL 路由扩展
+ - **生命周期管理**: Router 自动管理 ViewModel 生命周期
- **优势**:
- - 所有 ViewModel 符合 `IRoutableViewModel` 标准
- - 支持 ReactiveUI 导航历史管理
- - 为未来路由参数和 URL 支持打下基础
- - 更好的测试性和松耦合架构
- - 渐进式升级:保持现有 UI,底层已支持路由
+ - ✅ 完全符合 ReactiveUI 最佳实践
+ - ✅ 支持导航历史管理(Router 内置)
+ - ✅ 支持 URL/路由参数扩展
+ - ✅ 更好的测试性(Router 可模拟)
+ - ✅ 松耦合架构(ViewModel 与 View 解耦)
+ - ✅ 类型安全(编译期检查)
+ - ✅ 自动视图解析(RoutedViewHost)
### Avalonia.ReactiveUI 架构检查报告(已优化)
- **日期**: 2025年1月10日
diff --git a/obj/Debug/net9.0/Avalonia/Resources.Inputs.cache b/obj/Debug/net9.0/Avalonia/Resources.Inputs.cache
index 6c89e65..8451a03 100644
--- a/obj/Debug/net9.0/Avalonia/Resources.Inputs.cache
+++ b/obj/Debug/net9.0/Avalonia/Resources.Inputs.cache
@@ -1 +1 @@
-a9f27ce4bdac4bf5ca954160904d645cef7a4bde57bc6b530b0206ad45cc8eb7
+13ddcbc13e64d6798e8fba3e7da8f73f1d8b4a67a0cb18cbd7472bbf3213f169
diff --git a/obj/Debug/net9.0/Avalonia/resources b/obj/Debug/net9.0/Avalonia/resources
index 87872ce..aabe78e 100644
Binary files a/obj/Debug/net9.0/Avalonia/resources and b/obj/Debug/net9.0/Avalonia/resources differ
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfo.cs b/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfo.cs
index 5fe07f4..6388f5f 100644
--- a/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfo.cs
+++ b/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfo.cs
@@ -1,9 +1,10 @@
//------------------------------------------------------------------------------
//
-// This code was generated by a tool.
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
//
//------------------------------------------------------------------------------
@@ -13,10 +14,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+f91c1a3f1732694658082046c1b77e52cbf8b226")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0a6f31e4e28b92a7372c2f56ab37d30163b0e026")]
[assembly: System.Reflection.AssemblyProductAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyTitleAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
-// Generated by the MSBuild WriteCodeFragment class.
+// 由 MSBuild WriteCodeFragment 类生成。
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache b/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
index 84b6485..c664d05 100644
--- a/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
+++ b/obj/Debug/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
@@ -1 +1 @@
-f1734234089d69d76b866b0af754acd7ebdd6538242ae0e2515f28c7d86d79fc
+2e4e500ba7ddc7bd51e31ae31185388c392dd1b35d1e3220ad0285d08bdb3e15
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.csproj.CoreCompileInputs.cache b/obj/Debug/net9.0/MyAvaloniaApp.csproj.CoreCompileInputs.cache
index ad59161..ebce3ad 100644
--- a/obj/Debug/net9.0/MyAvaloniaApp.csproj.CoreCompileInputs.cache
+++ b/obj/Debug/net9.0/MyAvaloniaApp.csproj.CoreCompileInputs.cache
@@ -1 +1 @@
-0a16b036936ce872dfa8c92ae7b744f3a9f7b6c585a95920415d36320484cece
+4f19c6d987d7d083abf5effe7ae4b7a817f080aaf88ae19b49d56bb536cf1927
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.csproj.FileListAbsolute.txt b/obj/Debug/net9.0/MyAvaloniaApp.csproj.FileListAbsolute.txt
index 6a42624..0992762 100644
--- a/obj/Debug/net9.0/MyAvaloniaApp.csproj.FileListAbsolute.txt
+++ b/obj/Debug/net9.0/MyAvaloniaApp.csproj.FileListAbsolute.txt
@@ -5,9 +5,6 @@ D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.GeneratedMSBuildEditorConfig
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.AssemblyInfoInputs.cache
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.AssemblyInfo.cs
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.csproj.CoreCompileInputs.cache
-D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.dll
-D:\Log\MyAvaloniaApp\obj\Debug\net9.0\refint\MyAvaloniaApp.dll
-D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.pdb
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\MyAvaloniaApp.exe
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\appsettings.json
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\MyAvaloniaApp.deps.json
@@ -41,6 +38,7 @@ D:\Log\MyAvaloniaApp\bin\Debug\net9.0\Avalonia.Win32.Automation.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\Avalonia.Win32.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\Avalonia.X11.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\AvaloniaEdit.TextMate.dll
+D:\Log\MyAvaloniaApp\bin\Debug\net9.0\DialogHost.Avalonia.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\DynamicData.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\HarfBuzzSharp.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\HeroIconsAvalonia.dll
@@ -119,6 +117,8 @@ D:\Log\MyAvaloniaApp\bin\Debug\net9.0\runtimes\win-x86\native\libSkiaSharp.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\runtimes\win\lib\net9.0\System.Diagnostics.EventLog.Messages.dll
D:\Log\MyAvaloniaApp\bin\Debug\net9.0\runtimes\win\lib\net9.0\System.Diagnostics.EventLog.dll
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvalon.9DF80BA1.Up2Date
+D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.dll
+D:\Log\MyAvaloniaApp\obj\Debug\net9.0\refint\MyAvaloniaApp.dll
+D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.pdb
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\MyAvaloniaApp.genruntimeconfig.cache
D:\Log\MyAvaloniaApp\obj\Debug\net9.0\ref\MyAvaloniaApp.dll
-D:\Log\MyAvaloniaApp\bin\Debug\net9.0\DialogHost.Avalonia.dll
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.dll b/obj/Debug/net9.0/MyAvaloniaApp.dll
index 70b340d..d4136d0 100644
Binary files a/obj/Debug/net9.0/MyAvaloniaApp.dll and b/obj/Debug/net9.0/MyAvaloniaApp.dll differ
diff --git a/obj/Debug/net9.0/MyAvaloniaApp.pdb b/obj/Debug/net9.0/MyAvaloniaApp.pdb
index 3806fc2..5f8fc71 100644
Binary files a/obj/Debug/net9.0/MyAvaloniaApp.pdb and b/obj/Debug/net9.0/MyAvaloniaApp.pdb differ
diff --git a/obj/Debug/net9.0/apphost.exe b/obj/Debug/net9.0/apphost.exe
index 3bdea64..82b1b22 100644
Binary files a/obj/Debug/net9.0/apphost.exe and b/obj/Debug/net9.0/apphost.exe differ
diff --git a/obj/Debug/net9.0/ref/MyAvaloniaApp.dll b/obj/Debug/net9.0/ref/MyAvaloniaApp.dll
index 58164dd..3191dda 100644
Binary files a/obj/Debug/net9.0/ref/MyAvaloniaApp.dll and b/obj/Debug/net9.0/ref/MyAvaloniaApp.dll differ
diff --git a/obj/Debug/net9.0/refint/MyAvaloniaApp.dll b/obj/Debug/net9.0/refint/MyAvaloniaApp.dll
index 58164dd..3191dda 100644
Binary files a/obj/Debug/net9.0/refint/MyAvaloniaApp.dll and b/obj/Debug/net9.0/refint/MyAvaloniaApp.dll differ
diff --git a/obj/Release/net9.0/Avalonia/Resources.Inputs.cache b/obj/Release/net9.0/Avalonia/Resources.Inputs.cache
index 64d0211..5ba7776 100644
--- a/obj/Release/net9.0/Avalonia/Resources.Inputs.cache
+++ b/obj/Release/net9.0/Avalonia/Resources.Inputs.cache
@@ -1 +1 @@
-6db7f7d923d8c1cf44898db58528f845b13ca249bde1bdd63f24b6583b2fb618
+565664ee8d2e0eef575fae90c378130acfddf78d1f4c6886333b565cd40d783e
diff --git a/obj/Release/net9.0/Avalonia/resources b/obj/Release/net9.0/Avalonia/resources
index b8a2313..aabe78e 100644
Binary files a/obj/Release/net9.0/Avalonia/resources and b/obj/Release/net9.0/Avalonia/resources differ
diff --git a/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfo.cs b/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfo.cs
index 2bc59f2..fa33e18 100644
--- a/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfo.cs
+++ b/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfo.cs
@@ -1,10 +1,9 @@
//------------------------------------------------------------------------------
//
-// 此代码由工具生成。
-// 运行时版本:4.0.30319.42000
+// This code was generated by a tool.
//
-// 对此文件的更改可能会导致不正确的行为,并且如果
-// 重新生成代码,这些更改将会丢失。
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
//
//------------------------------------------------------------------------------
@@ -14,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
-[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
+[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+0a6f31e4e28b92a7372c2f56ab37d30163b0e026")]
[assembly: System.Reflection.AssemblyProductAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyTitleAttribute("MyAvaloniaApp")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
-// 由 MSBuild WriteCodeFragment 类生成。
+// Generated by the MSBuild WriteCodeFragment class.
diff --git a/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache b/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
index 74c7f54..ecee00a 100644
--- a/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
+++ b/obj/Release/net9.0/MyAvaloniaApp.AssemblyInfoInputs.cache
@@ -1 +1 @@
-3ff08d4c1a2a8d89d5383dc9190935873755fc8f8e541fbc6f37c55291a0c028
+8df7f0f8ce54bd535a9dcb9951bcb413cbed307269589f8803381a647c43aa9d
diff --git a/obj/Release/net9.0/MyAvaloniaApp.GeneratedMSBuildEditorConfig.editorconfig b/obj/Release/net9.0/MyAvaloniaApp.GeneratedMSBuildEditorConfig.editorconfig
index b3e37f2..c681ed2 100644
--- a/obj/Release/net9.0/MyAvaloniaApp.GeneratedMSBuildEditorConfig.editorconfig
+++ b/obj/Release/net9.0/MyAvaloniaApp.GeneratedMSBuildEditorConfig.editorconfig
@@ -27,5 +27,35 @@ build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
[D:/Log/MyAvaloniaApp/MainWindow.axaml]
build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+[D:/Log/MyAvaloniaApp/Resources/Colors.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Resources/Strings.en.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Resources/Strings.zh-CN.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
[D:/Log/MyAvaloniaApp/Views/Pages/DashboardPageView.axaml]
build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/DialogHostPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/EditorPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/HelpPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/IconsPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/ReportsPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/SettingsPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
+
+[D:/Log/MyAvaloniaApp/Views/Pages/UsersPageView.axaml]
+build_metadata.AdditionalFiles.SourceItemGroup = AvaloniaXaml
diff --git a/obj/Release/net9.0/MyAvaloniaApp.assets.cache b/obj/Release/net9.0/MyAvaloniaApp.assets.cache
index feaf62c..c898eb1 100644
Binary files a/obj/Release/net9.0/MyAvaloniaApp.assets.cache and b/obj/Release/net9.0/MyAvaloniaApp.assets.cache differ
diff --git a/obj/Release/net9.0/MyAvaloniaApp.csproj.AssemblyReference.cache b/obj/Release/net9.0/MyAvaloniaApp.csproj.AssemblyReference.cache
index 4061bce..b949f40 100644
Binary files a/obj/Release/net9.0/MyAvaloniaApp.csproj.AssemblyReference.cache and b/obj/Release/net9.0/MyAvaloniaApp.csproj.AssemblyReference.cache differ