前言
上一篇文章我们讲了在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));
}
}