前言
上一篇文章我们讲了在Avalonia开发中,引入Prism框架来完成项目的MVVM迁移。本章内容将带领大家学习如何在Avalonia中使用Prism框架实现区域导航功能。如果你还不知道Avalonia中如何引入Prism框架,请看我上一篇文章:Avalonia框架下面使用Prism框架实现MVVM模式
新建导航页
我们首先在相应的文件夹下面创建ViewA、ViewB以及他们所对应的ViewModel,创建好的目录结构如下:
创建View和WPF里面差不多,但是Avalonia需要手动的指定绑定的VM的DataType
类型。其实通过后面的验证,其实指定不指定其实在Prism框架下面没有影响,项目依然能够正常运行。但是有一个问题就是如果你设置了DataType类型,你在后期界面绑定数据的时候会有智能提示出来方便开发。
这里有个小细节需要 特别注意,这里面有个小bug,我们创建的ViewModel或者新引入nuget动态库文件以后,如果我们想在view里面进行引用,应该先全局生成一下解决方案,这样在编码的时候才能出现智能提示,希望官方后期能够修复这个bug.
创建ViewModel
创建ViewModel,这里面其实和WPF里面是一样的,这里就不在赘述,代码如下:
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AvaloniaTest.ViewModels
{public class ViewAViewModel : ViewModelBase, INavigationAware{private string _title="ViewA";public string Title{get => _title; set{SetProperty(ref _title, value);}}public bool IsNavigationTarget(NavigationContext navigationContext){return true;}public void OnNavigatedFrom(NavigationContext navigationContext){}public void OnNavigatedTo(NavigationContext navigationContext){}}
}
容器里面注册导航资源
我们在App.xaml里面注册导航用到的ViewA和ViewB,代码如下:
protected override void RegisterTypes(IContainerRegistry containerRegistry){containerRegistry.Register<MainView>();containerRegistry.RegisterForNavigation<ViewA,ViewAViewModel>(nameof(ViewA));containerRegistry.RegisterForNavigation<ViewB,ViewBViewModel>(nameof(ViewB));}
创建显示区域
在MainView里面创建显示区域,其实和WPF里面是一模一样的。
ViewModel里面的导航实现
这里面主要通过构造函数传过来一个IRegionManager
参数,然后通过这个区域管理对象对我们的区域实现导航功能,其实实现也都和WPF里面一样。
完整的代码实现
MainView.axaml
<UserControl xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:vm="clr-namespace:AvaloniaTest.ViewModels"xmlns:prism="http://prismlibrary.com/"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="AvaloniaTest.Views.MainView"prism:ViewModelLocator.AutoWireViewModel="True"x:DataType="vm:MainViewModel"><Design.DataContext><!-- This only sets the DataContext for the previewer in an IDE,to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) --></Design.DataContext><Grid RowDefinitions="50,*"><Grid ColumnDefinitions="*,*,*"><TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center"/><Button Content="ViewA" Grid.Column="1" Command="{Binding ViewACommand}"></Button><Button Content="ViewB" Grid.Column="2" Command="{Binding ViewBCommand}"></Button></Grid><ContentControl Grid.Row="1" prism:RegionManager.RegionName="mainContent"></ContentControl></Grid></UserControl>
MainViewModel.cs
using AvaloniaTest.Views;
using Prism.Commands;
using Prism.Regions;namespace AvaloniaTest.ViewModels;public class MainViewModel : ViewModelBase
{private readonly IRegionManager _regionManager;public MainViewModel(IRegionManager regionManager) {_regionManager = regionManager;_regionManager.RegisterViewWithRegion("mainContent", nameof(ViewB));ViewACommand = new DelegateCommand(() => {_regionManager.RequestNavigate("mainContent", nameof(ViewA));});ViewBCommand = new DelegateCommand(() => {_regionManager.RequestNavigate("mainContent", nameof(ViewB));});}public DelegateCommand ViewACommand { get; set; }public DelegateCommand ViewBCommand { get; set; }public string Greeting => "Welcome to Avalonia!";}
ViewA.axaml
<UserControl xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:vm="using:AvaloniaTest.ViewModels"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:DataType="vm:ViewAViewModel"x:Class="AvaloniaTest.Views.ViewA" Background="Red"><TextBlock Text="{Binding Title}"></TextBlock>
</UserControl>
ViewAViewModel.cs
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AvaloniaTest.ViewModels
{public class ViewAViewModel : ViewModelBase, INavigationAware{private string _title="ViewA";public string Title{get => _title; set{SetProperty(ref _title, value);}}public bool IsNavigationTarget(NavigationContext navigationContext){return true;}public void OnNavigatedFrom(NavigationContext navigationContext){}public void OnNavigatedTo(NavigationContext navigationContext){}}
}
ViewB.axaml
<UserControl xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:prism="http://prismlibrary.com/"prism:ViewModelLocator.AutoWireViewModel="True"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="AvaloniaTest.Views.ViewB" Background="Green"><TextBlock Text="{Binding Title}"></TextBlock>
</UserControl>
ViewBViewModel.cs
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace AvaloniaTest.ViewModels
{public class ViewBViewModel : ViewModelBase, INavigationAware{private string _title = "ViewB";public string Title{get => _title;set{SetProperty(ref _title, value);}}public bool IsNavigationTarget(NavigationContext navigationContext){return true;}public void OnNavigatedFrom(NavigationContext navigationContext){}public void OnNavigatedTo(NavigationContext navigationContext){}}
}
App.axaml
<prism:PrismApplication xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:prism="http://prismlibrary.com/"x:Class="AvaloniaTest.App"RequestedThemeVariant="Default"><!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --><Application.Styles><FluentTheme /></Application.Styles>
</prism:PrismApplication>
App.axaml.cs
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using AvaloniaTest.ViewModels;
using AvaloniaTest.Views;
using Prism.DryIoc;
using Prism.Ioc;namespace AvaloniaTest;public partial class App : PrismApplication
{public override void Initialize(){AvaloniaXamlLoader.Load(this);base.Initialize();}public override void OnFrameworkInitializationCompleted(){//if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)//{// desktop.MainWindow = new MainWindow// {// DataContext = new MainViewModel()// };//}//else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)//{// singleViewPlatform.MainView = new MainView// {// DataContext = new MainViewModel()// };//}base.OnFrameworkInitializationCompleted();}protected override AvaloniaObject CreateShell(){return Container.Resolve<MainWindow>();}protected override void RegisterTypes(IContainerRegistry containerRegistry){containerRegistry.Register<MainView>();containerRegistry.RegisterForNavigation<ViewA,ViewAViewModel>(nameof(ViewA));containerRegistry.RegisterForNavigation<ViewB,ViewBViewModel>(nameof(ViewB));}
}