1.创建一个Prism框架的项目并设计好数据源
User如下:
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF练习17区域导航.Models
{
public class User
{
public int UserId { get; set; }
public string UserName { get; set; }
public string UserPwd { get; set; }
}
}
UserData如下:
cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF练习17区域导航.Models
{
public static class UserData
{
public static List<User> Users = new List<User>() {
new User(){ UserId=1,UserName="张三1",UserPwd="123456"},
new User(){ UserId=2,UserName="张三2",UserPwd="123456"}
};
}
}
2.设计好MainWindow.XAML页面 并创建四个UserControl的导航页面以及相应的ViewModel然后将视图注册进Ioc容器
MainWindow.XAML如下:
XML
<Window x:Class="WPF练习17区域导航.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}"
Height="350"
Width="525"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Background="Blue">
<WrapPanel VerticalAlignment="Center">
<TextBlock VerticalAlignment="Center"
FontSize="30"
Foreground="White"
Text="XXX管理系统" />
<Button Width="50"
Margin="10,0,0,0"
Command="{Binding BackCommand}"
Content="后退" />
<Button Width="50"
Margin="10,0,0,0"
Command="{Binding ForwardCommand}"
Content="前进" />
</WrapPanel>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="160" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Background="Green">
<Button Height="30"
Margin="0,10,0,0"
Command="{Binding TogglePage}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Content="PageA" />
<Button Height="30"
Margin="0,10,0,0"
Command="{Binding TogglePage}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Content="PageB" />
<Button Height="30"
Margin="0,10,0,0"
Command="{Binding TogglePage}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Content="PageC" />
</StackPanel>
<Border Grid.Column="1"
Padding="10"
BorderBrush="Red"
BorderThickness="2">
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Border>
</Grid>
</Grid>
</Window>
创建四个UserControl的导航页面:
将视图注册进Ioc容器:
3.四个页面设计好以后 写好MainWIndow对应的ViewModel代码
PageA如下:
XML
<UserControl x:Class="WPF练习17区域导航.Views.Pages.PageA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF练习17区域导航.Views.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="查询条件区域" />
</StackPanel>
<DataGrid Grid.Row="1"
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding Users}">
<DataGrid.Columns>
<DataGridTextColumn Width="*"
Binding="{Binding UserId}"
Header="编号" />
<DataGridTextColumn Width="*"
Binding="{Binding UserName}"
Header="账号" />
<DataGridTextColumn Width="*"
Binding="{Binding UserPwd}"
Header="密码" />
<DataGridTemplateColumn Width="3*"
Header="操作">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<WrapPanel>
<Button Width="50"
Margin="5,0,0,0"
Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=UserControl}}"
CommandParameter="{Binding UserId}"
Content="编辑" />
<Button Width="50"
Margin="5,0,0,0"
Content="删除" />
<Button Width="70"
Margin="5,0,0,0"
Content="分配权限" />
<Button Width="50"
Margin="5,0,0,0"
Content="详情" />
</WrapPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2"
VerticalAlignment="Center">
<TextBlock Text="分页区域" />
</StackPanel>
</Grid>
</UserControl>
PageAViewModel如下:
cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WPF练习17区域导航.Models;
namespace WPF练习17区域导航.ViewModels.Pages
{
public class PageAViewModel: BindableBase, INavigationAware, IConfirmNavigationRequest
{
private readonly IRegionManager regionManager;
public PageAViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
private List<User> users = UserData.Users;
public List<User> Users
{
get { return users; }
set { SetProperty(ref users, value); }
}
public DelegateCommand<object> EditCommand
{
get
{
return new DelegateCommand<object>((obj) =>
{
int userId = (int)obj;
// 导航到编辑页面,并且需要向编辑页面传递参数UserId
NavigationParameters parameters = new NavigationParameters
{
{ "uid", userId }
};
// 通过NavigationParameters对象向导航传递参数
this.regionManager.Regions["ContentRegion"].RequestNavigate("PageAEdit", parameters);
});
}
}
#region INavigationAwre
// 第一次进入页面不会执行,第2次之后都会进入。
public bool IsNavigationTarget(NavigationContext navigationContext)
{
//MessageBox.Show("PageA IsNavigationTarget!");
return false;
}
// 站在当前页面的角度:OnNavigatedFrom离开当前页面
public void OnNavigatedFrom(NavigationContext navigationContext)
{
//MessageBox.Show("离开PageA!");
}
// 站在当前页面的角度:OnNavigatedTo到当前页面
public void OnNavigatedTo(NavigationContext navigationContext)
{
//MessageBox.Show("到PageA!");
}
#endregion
#region IConfirmNavigationRequest
// 时机:如果从PageA跳转到PageAEdit页面,需要进行导航确认,应该把导航确认的业务逻辑流写到来源页面。
// 导航时,确认是否允许导航动作。
// navigationContext导航上下文对象。
// continuationCallback回调函数。
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
if (navigationContext.Uri.ToString() == "PageAEdit")
{
MessageBoxResult mbr = MessageBox.Show("是否允许进入PageAEdit页面?", "询问", MessageBoxButton.YesNo, MessageBoxImage.Question);
bool isAllow = false;
if (mbr == MessageBoxResult.Yes)
{
isAllow = true;
}
continuationCallback(isAllow);
}
else
{
continuationCallback(true);
}
}
#endregion
}
}
在WPF中使用Prism框架时,INavigationAware
和IConfirmNavigationRequest
是两个重要的接口,它们允许视图或视图模型参与导航过程,提供了对导航行为的细粒度控制。
INavigationAware
INavigationAware
接口包含三个方法,用于在导航过程中与视图或视图模型进行交互:
bool IsNavigationTarget(NavigationContext navigationContext)
:当导航到视图或视图模型时,该方法被调用,以确定当前实例是否可以处理导航请求。通常返回true
,表示当前视图可以处理导航。void OnNavigatedTo(NavigationContext navigationContext)
:当视图被导航到的时候,该方法被调用。可以用来初始化视图或处理传入的参数。void OnNavigatedFrom(NavigationContext navigationContext)
:当视图被导航离开时,该方法被调用。可以用来保存状态或清理资源。
IConfirmNavigationRequest
IConfirmNavigationRequest
接口继承自INavigationAware
,并添加了一个方法**ConfirmNavigationRequest
,用于在导航发生前与用户交互,以确认或取消导航:**
void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
:该方法提供了两个参数,一个是当前导航上下文,另一个是一个回调方法。在调用该方法时,可以显示一个确认对话框,根据用户的选择调用回调方法以决定是否继续导航。例如,如果用户选择取消,则导航将被取消。
PageAEdit.XAML如下:
XML
<UserControl x:Class="WPF练习17区域导航.Views.Pages.PageAEdit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF练习17区域导航.Views.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel>
<WrapPanel Margin="0,10,0,0">
<TextBlock Text="账号:" />
<TextBox Width="150"
Text="{Binding CurrentEditUser.UserName}" />
</WrapPanel>
<WrapPanel Margin="0,10,0,0">
<TextBlock Text="密码:" />
<TextBox Width="150"
Text="{Binding CurrentEditUser.UserPwd}" />
</WrapPanel>
<WrapPanel Margin="0,10,0,0">
<Button Width="50"
Content="保存" />
</WrapPanel>
<WrapPanel Margin="0,10,0,0">
<Button Width="160"
Command="{Binding GoOtherEditCommand}"
CommandParameter="2"
Content="详情页面跳转到其他详情" />
</WrapPanel>
</StackPanel>
</Grid>
</UserControl>
PageAEditViewModel如下:
cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WPF练习17区域导航.Models;
namespace WPF练习17区域导航.ViewModels.Pages
{
// 在PageAEdit页面接收到UserId才能处理下一步的逻辑。
// 一个跳转目标(PageAEdit页面)要想接收传递过来的参数:必须实现INavigationAware
public class PageAEditViewModel : BindableBase, INavigationAware
{
private readonly IRegionManager regionManager;
public PageAEditViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
private User currentEditUser;
public User CurrentEditUser
{
get { return currentEditUser; }
set { SetProperty(ref currentEditUser, value); }
}
#region INavigationWare
// IsNavigationTarget()第一次进入页面不会执行,第2次之后才会进入。
// 通过调试,会发现IsNavigationTarget()方法返回true或false达到效果是一样。但是性能不一样。
// 如果返回true,说明不会重新创建视图实例,不会重新创建导航上下文,即:IsNavigationTarget()方法中使用的视图实例及上下文会在OnNavigatedTo()方法中共用。
// 如果返回false,说明会重新创建视图实例,会重新创建导航上下文,即:IsNavigationTarget()方法中使用的视图实例及上下文和OnNavigatedTo()方法中使用的视图实例及上下文不是同一个实例。
/*IsNavigationTarget()方法用来控制视图或者视图模型实例在导航时是否被重用。当你尝试导航到一个新的视图的时候,Prism会检查当前活动的视图或者视图模型的实例,并调用他们的IsNavigationTarget方法,以决定是否可以重用这个实例。如果返回true,Prism框架将不会创建新的视图或者视图模型实例,而是重用当前的实例。这种方式非常适用于那些数据和状态不需要每次都重新加载的视图,可以提高应用程序的响应速度和资源利用率。
*/
public bool IsNavigationTarget(NavigationContext navigationContext)
{
Console.WriteLine(navigationContext);
//MessageBox.Show("PageAEdit IsNavigationTarget!");
return true;
}
// 站在当前页面的角度:OnNavigatedFrom离开当前页面
public void OnNavigatedFrom(NavigationContext navigationContext)
{
//MessageBox.Show("离开PageAEdit!");
}
// 站在当前页面的角度:OnNavigatedTo到当前页面
// 拿参数的时机:在进入当前页面时,拿传递的参数最合理。
// 如何拿参数?NavigationContext导航的上下文,拿到上下文就可以拿Uri, Parameters, NavigationService 操作API(导航服务)
public void OnNavigatedTo(NavigationContext navigationContext)
{
object obj = navigationContext.Parameters["uid"]; // 取传值方式1
int uid1 = (int)obj;
int uid2 = navigationContext.Parameters.GetValue<int>("uid");//取传值方式2
// AsQueryable()转换成IQueryable<T>,由于IQueryable<T>继承IEnumerable<T>接口,从而可以调用LINQ扩展方法。
CurrentEditUser = UserData.Users.AsQueryable().Where(u => u.UserId == uid1).FirstOrDefault();
//MessageBox.Show("到PageAEdit!");
}
#endregion
public DelegateCommand<object> GoOtherEditCommand
{
get
{
return new DelegateCommand<object>(obj =>
{
int uid = Convert.ToInt32(obj);
NavigationParameters parameters = new NavigationParameters()
{
{"uid",uid}
};
this.regionManager.RequestNavigate("ContentRegion", "PageAEdit", parameters);
});
}
}
}
}
PageB如下:
XML
<UserControl x:Class="WPF练习17区域导航.Views.Pages.PageB"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF练习17区域导航.Views.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="PageB" />
</Grid>
</UserControl>
PageBViewModel如下:
cs
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 区域导航.ViewModels.Pages
{
public class PageBViewModel : BindableBase
{
}
}
PageC如下:
XML
<UserControl x:Class="WPF练习17区域导航.Views.Pages.PageC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WPF练习17区域导航.Views.Pages"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="PageC" />
</Grid>
</UserControl>
PageCViewModel如下:
cs
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WPF练习17区域导航.ViewModels.Pages
{
public class PageCViewModel : BindableBase
{
}
}
4.最后写好MainWindowViewModel代码
cs
using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using System.Windows.Controls;
namespace WPF练习17区域导航.ViewModels
{
public class MainWindowViewModel : BindableBase
{
// 导航日志服务,主要负责提供导航日志的管理,如:添加导航日志,根据导航日志可以前进,后退等等。
// 添加导航日志的时机,应该在导航跳转成功后,添加导航日志。
private IRegionNavigationJournal journal;
private readonly IRegionManager regionManager;
// 使用依赖注入的把区域管理服务注入当前的VM。
public MainWindowViewModel(IRegionManager regionManager, IRegionNavigationJournal journal)
{
this.regionManager = regionManager;
this.journal = journal;
}
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public DelegateCommand<object> TogglePage
{
get
{
return new DelegateCommand<object>(obj =>
{
Button button = obj as Button;
// 导航跳转
// 前提:需要把视图先扔到Ioc容器
// 1。拿区域管理器(其实是区域给VM提供的一种服务而异)
// 2。从区域管理器中拿某个区域
// 3。调用区域的API(导航服务)
// 参数1:导航的目标视图名称。参数2:导航后(有可能成功,也有可能失败)的回调函数。参数3:导航参数。
this.regionManager.Regions["ContentRegion"].RequestNavigate(
button.Content.ToString(),
NavigationCallback,
new NavigationParameters()
);
});
}
}
private void NavigationCallback(NavigationResult navigationResult)
{
// 导航成功
//if(navigationResult.Result==true)
//if((bool)navigationResult.Result)
if (navigationResult.Result.Value == true)
{
// 添加导航日志的时机,应该在导航跳转成功后,添加导航日志。
// navigationResult.Context先拿导航上下文。
// NavigationService导航服务
// Journal日志
journal = navigationResult.Context.NavigationService.Journal;
}
}
public DelegateCommand ForwardCommand
{
get
{
return new DelegateCommand(() =>
{
if (journal != null && journal.CanGoForward)
{
journal.GoForward();
}
});
}
}
public DelegateCommand BackCommand
{
get
{
return new DelegateCommand(() =>
{
if (journal != null && journal.CanGoBack)
{
journal.GoBack();
}
});
}
}
}
}