文章目录
- 前言
- Prism基本使用
- Prism选择,DryIoc还是Unity
- Prism基本框架搭建
- Prism动态更新
- View和ViewModel对应关系
- 参数动态更新
- 函数动态绑定
- prism新建项目模板
- region
- 使用事例
- 测试是否限制空间
- 消息订阅
- 如何使用消息订阅
- 使用建议
- 路由导航
- 对话框/弹窗功能
- 实现代码
前言
Prims框架是WPF的一个框架,特点是集成了我们平常常用的开发模式,封装了很多常用的功能。例如消息通知,路由导航,Model绑定。
Prism基本使用
资料来源
WPF-Prism8.0核心教程(公益)
Prism选择,DryIoc还是Unity
根据网上的说明,DryIoc是更加效率更高的,但是我看Nuget下载数量里面,Unity下载数量比DryIoc高得多。具体没怎么用过,那就按照推荐用DryIoc好了。
Unity和DryIoc之间性能比较
Prism基本框架搭建
在App.xaml里面,修改
App.xaml.cs
/// <summary>
/// 继承prism的PrismApplication类
/// </summary>
public partial class App : PrismApplication
{
//设置启动页
protected override Window CreateShell()
{
//启动页为MainWindow
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
App.xaml
<!--xmlns:prism,引入prism命名空间-->
<!--prism:PrismApplication,继承prism下的PrismApplication类-->
<!--去掉StartupUrl,保证只有一个启动页-->
<prism:PrismApplication x:Class="PrismTest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrismTest"
xmlns:prism="http://prismlibrary.com/">
</prism:PrismApplication>
Prism动态更新
View和ViewModel对应关系
View和ViewModel可以自动绑定,命名要求为。
- Model:NameViewModel
- View:Name/NameView(两者都可以)
在View中添加入如下代码
xmlns:prism引入命名空间
prism:ViewModel设置自动绑定
可以不加,默认进行了绑定。
<Window ...
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
也可以在App.xaml里面强行绑定
在原文件件中取消绑定
app.xaml
/// <summary>
/// 路由管理
/// </summary>
/// <param name="containerRegistry"></param>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//通过RegisterForNavigation进行强行绑定,强行让ViewA和ViewAViewModel进行绑定
containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
}
参数动态更新
//继承BindableBase接口
public class MyListBoxItem : BindableBase
{
private string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value);}//此方法为Prism提供
}
}
可以输入propp加tab键,模板输入,提交写代码效率
函数动态绑定
函数动态绑定需要用到委托类型,如果委托不明白需要自己回去了解一下
viewModel
//声明委托
public DelegateCommand TestBtn { get; set; }
//构造函数
public MainWindowViewModel()
{
//实例化委托
TestBtn = new DelegateCommand(() =>
{
//执行内容
});
}
view
使用Command引用委托,如果没有TestBtn委托也不报错
<Button Content="测试方法"
FontSize="100"
Grid.Row="1"
Command="{Binding TestBtn}"/>
prism新建项目模板
在扩展,管理扩展中安装
我们在新建程序的时候会有Prism模板框架
使用时会提示使用什么容器,这里推荐DryIoc容器
新建文件路径如下
region
Prism将界面继续划分,分为多个Region,在Region中添加元素。
和WPF用户组件的区别。
- Region相当于划分了空区域,WPF是有内容的区域。
- Region相当于WPF用户控件的上级,用于包含用户控件
使用事例
MainWindow.xaml
<Grid>
<!--声明Region,名称为ContentRegion,使用名称来进行注入-->
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
UserControl1.xaml,用于放置在Region上面的用户控件
<Grid>
<TextBlock Text="我是用户控件" FontSize="100"/>
</Grid>
MainWindowViewModel.xaml
//注册regionManager控件
private readonly IRegionManager _regionManager;
public MainWindowViewModel(IRegionManager regionManager)
{
this._regionManager = regionManager;
//ContentRegion是窗口已声明的Region,使用字符串进行弱绑定
regionManager.RegisterViewWithRegion("ContentRegion",typeof(UserControl1));
}
生成效果
测试是否限制空间
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!--声明Region,名称为ContentRegion,使用名称来进行注入-->
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
实现效果
消息订阅
消息订阅是先订阅再发布有效,如果是先发布再订阅则无效。
如何使用消息订阅
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
//声明委托
public DelegateCommand TestBtn { get; set; }
public DelegateCommand SendCommand { get; set; }
private readonly IEventAggregator _eventAggregator;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
//构造函数
public MainWindowViewModel(IRegionManager regionManager,IEventAggregator eventAggregator)
{
//实例化委托
//订阅
TestBtn = new DelegateCommand(() =>
{
eventAggregator.GetEvent<MessageEvent>().Subscribe(OnMessageRecevied);
});
//发送
SendCommand = new DelegateCommand(() =>
{
//执行参数
eventAggregator.GetEvent<MessageEvent>().Publish("Hello Event");
});
this._eventAggregator = eventAggregator;
}
public void OnMessageRecevied(string msg)
{
Title += msg + "\r\n";
}
//消息订阅类
public class MessageEvent: PubSubEvent<string> {
}
}
注意
eventAggregator.GetEvent<MessageEvent>()。MessageEvent是我们自己定义的,相当于订阅的消息主题。
eventAggregator.GetEvent().
- Subscribe(OnMessageRecevied);
- OnMessageRecevied是处理函数
- Publish(“Hello Event”)
- "Hello Event"是入参,因为OnMessageRecevied(string msg)
取消订阅
eventAggregator.GetEvent<MessageEvent>().Unsubscribe(OnMessageRecevied);
使用建议
- 注意,这个是全局使用的,所以一般用于页面通讯
- 例如:AView。我就用AViewEvent。一个页面对应一个事件通知。
- 由于带入的参数不一定,所以我们可以设置Dictionary为入参,通过key值过滤来设置对应的函数。通过NetJson来快速传参。
路由导航
路由导航和Region是搭配使用的,专门用于页面转化
- 新建切换用的用户控件ViewA,ViewB,ViewC。ViewA,B,C内容有区分,这里省略
- 在App.xaml.cs中注册页面
/// <summary>
/// 路由管理
/// </summary>
/// <param name="containerRegistry"></param>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
//将ViewA,B,C添加到导航,这样才能进行路由管理
containerRegistry.RegisterForNavigation<ViewA>("ViewA");
containerRegistry.RegisterForNavigation<ViewB>();
containerRegistry.RegisterForNavigation<ViewC>();
}
- 在MainView中添加委托事件
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Content="ViewA"
Command="{Binding OpenACommand}" />
<Button Content="ViewB"
Command="{Binding OpenBCommand}" />
<Button Content="ViewC"
Command="{Binding OpenC_Command}" />
<Button Content="上一页"
Command="{Binding GoBackCommand}" />
<Button Content="下一页"
Command="{Binding GoForwordCommand}"/>
</StackPanel>
<!--这个Region是我们等会切换窗口信息的-->
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
在MainiView里面管理导航
namespace PrismNavigation.ViewModels
{
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
/// <summary>
/// 导航日志
/// </summary>
private IRegionNavigationJournal _journal;
//Region,用于页面切换
private readonly IRegionManager _regionManager;
public DelegateCommand OpenACommand { get; set; }
public DelegateCommand OpenBCommand { get; set; }
public DelegateCommand OpenC_Command { get; set; }
public DelegateCommand GoBackCommand { get; set; }
public DelegateCommand GoForwordCommand { get; set; }
public MainWindowViewModel(IRegionManager regionManager,IEventAggregator eventAggregator)
{
_regionManager = regionManager;
OpenACommand = new DelegateCommand(OpenA);
OpenBCommand = new DelegateCommand(OpenB);
OpenC_Command = new DelegateCommand(OpenC);
GoBackCommand = new DelegateCommand(GoBack);
GoForwordCommand = new DelegateCommand(GoForword);
}
private void OpenA()
{
NavigationParameters para = new NavigationParameters
{
{ "Value", "Hello Prism" }
};
//进行路由传参,ContentRegion是xmal中的Region名称,ViewA是在App.xmal中注册的名称,arg是日志回调,para是路由传递的参数
_regionManager.RequestNavigate("ContentRegion", "ViewA", arg =>
{
///获取导航日志
_journal = arg.Context.NavigationService.Journal;
}, para);
}
private void OpenB()
{
_regionManager.RequestNavigate("ContentRegion", "ViewB", arg =>
{
///获取导航日志
_journal = arg.Context.NavigationService.Journal;
});
}
private void OpenC()
{
_regionManager.RequestNavigate("ContentRegion", "ViewC", arg =>
{
///获取导航日志
_journal = arg.Context.NavigationService.Journal;
});
}
private void GoForword()
{
_journal.GoForward();
}
private void GoBack()
{
_journal.GoBack();
}
}
}
在ViewA中进行路由拦截
public class ViewAViewModel :BindableBase, IConfirmNavigationRequest
{
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
/// <summary>
/// IConfirmNavigationRequest的路由离开拦截,
/// </summary>
/// <param name="navigationContext"></param>
/// <param name="continuationCallback"></param>
/// <exception cref="NotImplementedException"></exception>
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
var res = true;
if(MessageBox.Show("确认导航?","温馨提示",MessageBoxButton.YesNo) == MessageBoxResult.No)
{
res = false;
}
//如果res为true则拦截,true则放行
continuationCallback?.Invoke(res);
}
/// <summary>
/// 用于判断是否要重新创建新的事例,即是否保留之前的信息,True为重新创建。
/// </summary>
/// <param name="navigationContext"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
/// <summary>
/// 离开页面时触发,一般用于取消事件监听
/// </summary>
/// <param name="navigationContext"></param>
/// <exception cref="NotImplementedException"></exception>
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
/// <summary>
/// 显示页面前触发,一般用于设置事件监听
/// </summary>
/// <param name="navigationContext"></param>
/// <exception cref="NotImplementedException"></exception>
public void OnNavigatedTo(NavigationContext navigationContext)
{
//拿到Key为Value的值,返回类型为string
Name = navigationContext.Parameters.GetValue<string>("Value");
}
}
实现效果
简单介绍
- 路由导航是Region导航,必须要先将WPF用户控件添加到Region中才能进行导航
- 在注册时,可以添加别名
- containerRegistry.RegisterForNavigation<ViewA>(“别名”);
_regionManager.RequestNavigate("ContentRegion", "别名");//原本是ViewA的,可以替换成别名
对话框/弹窗功能
Prism将对话框这个常用的功能进行了封装。虽然叫对话框,但其实是弹窗的功能效果。
实现代码
app.xaml中
/// <summary>
/// 路由管理
/// </summary>
/// <param name="containerRegistry"></param>
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
.......
//这里也能起别名
containerRegistry.RegisterDialog<MsgView,MsgViewModel>("别名");
}
MainViewModel使用这个弹窗
public class MainWindowViewModel : BindableBase
{
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
/// <summary>
/// 导航日志
/// </summary>
private IRegionNavigationJournal _journal;
private readonly IRegionManager _regionManager;
//注册弹窗服务
private IDialogService _dialogService;
public DelegateCommand OpenACommand { get; set; }
//构造函数中传入对话框
public MainWindowViewModel(IRegionManager regionManager,IEventAggregator eventAggregator,IDialogService dialogService)
{
_regionManager = regionManager;
OpenACommand = new DelegateCommand(OpenA);
GoForwordCommand = new DelegateCommand(GoForword);
//使用对话框
_dialogService = dialogService;
}
private void OpenA()
{
//对话框传递的参数
DialogParameters param = new DialogParameters();
param.Add("Value", "paramTest");
//打开名为MsgView的弹窗,并传递参数
_dialogService.Show("MsgView", param, arg =>
{
//弹窗关闭的回调函数,会有参数返回
if(arg.Result == ButtonResult.OK)
{
Debug.WriteLine("调试成功");
}
});
}
}
MsgView
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition />
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" Text="{Binding Title}" FontSize="20"/>
<Label Content="{Binding Title}"/>
<DockPanel Grid.Row="2" LastChildFill="False">
<Button Content="取消" Width="60"
DockPanel.Dock="Right"
Command="{Binding CancelCommand}" />
<Button Content="确定"
Width="60"
DockPanel.Dock="Right"
Command="{Binding SaveCommand}"/>
</DockPanel>
</Grid>
public class MsgViewModel :BindableBase, IDialogAware
{
private string _title;
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
/// <summary>
/// 用于弹窗关闭的回调函数
/// </summary>
public event Action<IDialogResult> RequestClose;
public DelegateCommand SaveCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
public MsgViewModel() {
SaveCommand = new DelegateCommand(() =>
{
DialogParameters keyValuePairs = new DialogParameters();
keyValuePairs.Add("Value", Title);
//执行弹窗关闭,并传入回调参数
RequestClose?.Invoke(new DialogResult(ButtonResult.OK, keyValuePairs));
});
CancelCommand = new DelegateCommand(() =>
{
RequestClose?.Invoke(new DialogResult(ButtonResult.No));
});
}
/// <summary>
/// 能否打开窗口
/// </summary>
/// <returns></returns>
public bool CanCloseDialog()
{
return true;
}
/// <summary>
/// 关闭的方法
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void OnDialogClosed()
{
//throw new NotImplementedException();
}
/// <summary>
/// 打开之前,接受弹窗传参
/// </summary>
/// <param name="parameters"></param>
/// <exception cref="NotImplementedException"></exception>
public void OnDialogOpened(IDialogParameters parameters)
{
Title = parameters.GetValue<string>("Value");
}
}