C#之Prism🔥
# 什么是Prism
Prism是一个微软的开源框架,旨在帮助开发人员构建具有高度可维护性和可扩展性的WPF应用程序。它不仅支持MVVM模式,而且还提供了很多组件来简化复杂应用的构建。通过Prism,我们可以更好地实现模块化、解耦和依赖注入。它提供了许多工具和扩展,使得MVVM模式能够更容易实现和管理。比如,Prism提供了事件聚合器(EventAggregator)用于解耦不同部分的交互、命令和视图之间的绑定等。源码地址:https://github.com/PrismLibrary/Prism (opens new window)
# 引入Prism
# a. 手动引入
1、创建WPF项目,在Nuget添加包Prism.DryIoc
2、改写App.xaml.cs
public partial class App : Application
//改成
public partial class App : PrismApplication
//实现抽象接口
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();//启动页面,如果想要启动别的页面可以在这里改动
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
3、改写App.xaml
<prism:PrismApplication x:Class="PrismDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PrismDemo"
xmlns:prism="http://prismlibrary.com/"
>
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>
2
3
4
5
6
7
8
9
10
4、新建Views和ViewModels文件夹,将View和ViewModel放在各自对应的文件夹,文件夹的名字一定要是Views和ViewModels!!!,不然可能会View可能会找不到ViewModel
5、ViewModel继承BindableBase,实现绑定
public class MainWindowViewModel:BindableBase
{
private string title = "PrismDemo";
public string Title
{
get { return title; }
set { SetProperty(ref title,value); }
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public void ButtonCommand(string command)
{
switch (command)
{
case "测试1":
Title = "测试1";
MessageBox.Show($"测试1");
break;
case "测试2":
MessageBox.Show($"测试2");
break ;
default:
break;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
在View中添加prism:ViewModelLocator.AutoWireViewModel="True",他会在Viewmodels下找到符合的ViewModel
<Window x:Class="PrismDemo.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:PrismDemo"
xmlns:prism="http://prismlibrary.com/"
mc:Ignorable="d"
Title="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="450" Width="800"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewmodel="clr-namespace:PrismDemo.ViewModels"
d:DataContext="{d:DesignInstance Type =viewmodel:MainWindowViewModel}">
<DockPanel LastChildFill="True">
<TextBlock Text="{Binding Title }" FontSize="60" VerticalAlignment="Center" HorizontalAlignment="Center" DockPanel.Dock="Top"/>
<Button Content="测试" Command="{Binding ButtonDelegateCommand }" CommandParameter="测试1"/>
<Button Content="测试2" Command="{Binding ButtonDelegateCommand }" CommandParameter="测试2"/>
</DockPanel>
</Window>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
至此手动引入就成功了。
# b. 安装Prism模板
VS工具栏=》扩展(X)=》管理扩展,安装Prism模板
新建WPF-Prism项目,就可以实现上面的功能了
# 属性和命令绑定
ViewModel继承BindableBase
public class MainWindowViewModel:BindableBase
{
private string title = "PrismDemo";
public string Title
{
get { return title; }
set { SetProperty(ref title,value); }
}
public MainWindowViewModel()
{
ButtonDelegateCommand = new DelegateCommand<string>(ButtonCommand);
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public void ButtonCommand(string command)
{
switch (command)
{
case "测试1":
Title = "测试1";
MessageBox.Show($"测试1");
break;
case "测试2":
MessageBox.Show($"测试2");
break ;
default:
break;
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<Window x:Class="PrismDemo.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:PrismDemo"
xmlns:prism="http://prismlibrary.com/"
mc:Ignorable="d"
Title="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="450" Width="800"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewmodel="clr-namespace:PrismDemo.ViewModels"
d:DataContext="{d:DesignInstance Type =viewmodel:MainWindowViewModel}">
<DockPanel LastChildFill="True">
<TextBlock Text="{Binding Title }" FontSize="60" VerticalAlignment="Center" HorizontalAlignment="Center" DockPanel.Dock="Top"/>
<Button Content="测试" Command="{Binding ButtonDelegateCommand }" CommandParameter="测试1"/>
<Button Content="测试2" Command="{Binding ButtonDelegateCommand }" CommandParameter="测试2"/>
</DockPanel>
</Window>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 区域
Prism的地区管理功能允许你在同一个视图中管理多个区域。区域是视图的容器,可以动态加载和卸载视图,这使得应用程序能够灵活地组织界面。
新建ViewA和ViewB用户控件,在APP.xmal.cs中注册
containerRegistry.RegisterForNavigation<ViewA>("ViewA");
containerRegistry.RegisterForNavigation<ViewB>("ViewB");
2
public class MainWindowViewModel:BindableBase
{
private string title = "PrismDemo";
public string Title
{
get { return title; }
set { SetProperty(ref title,value); }
}
private readonly IRegionManager regionManager;
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
ButtonDelegateCommand = new DelegateCommand<string>(ButtonCommand);
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public void ButtonCommand(string command)
{
regionManager.Regions["ContentRegion"].RequestNavigate(command);
Title = command;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<Window x:Class="PrismDemo.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:PrismDemo"
xmlns:prism="http://prismlibrary.com/"
mc:Ignorable="d"
Title="{Binding Title, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="450" Width="800"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewmodel="clr-namespace:PrismDemo.ViewModels"
d:DataContext="{d:DesignInstance Type =viewmodel:MainWindowViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<UniformGrid Rows="1" Columns="10" Margin="10">
<TextBlock Text="{Binding Title }" FontSize="12" Width="100" Height="20" VerticalAlignment="Top"/>
<Button Content="ViewA" Command="{Binding ButtonDelegateCommand }" CommandParameter="ViewA" Width="40" Height="20" VerticalAlignment="Top"/>
<Button Content="ViewB" Command="{Binding ButtonDelegateCommand }" CommandParameter="ViewB" Width="40" Height="20" VerticalAlignment="Top"/>
</UniformGrid>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>
</Grid>
</Window>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 导航
Prism中的导航主要用于实现应用程序中的视图之间的切换,特别是在多视图和模块化的应用程序中,它帮助管理不同视图之间的关系与切换。导航系统不仅支持视图之间的跳转,还支持传递参数、堆栈式导航和历史记录等功能,非常适合用于构建复杂的、交互丰富的用户界面。
# a.导航传参
要接收导航参数的控件以及视图,ViewModel实现INavigationAware接口
<UserControl x:Class="PrismDemo.Views.ViewA"
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:PrismDemo.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewmodel="clr-namespace:PrismDemo.ViewModels"
d:DataContext="{d:DesignInstance Type =viewmodel:ViewAViewModel}">
<Grid Background="red">
<TextBlock Text="{Binding Text}" FontSize="60"/>
</Grid>
</UserControl>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ViewAViewModel : BindableBase,INavigationAware
{
private string text = "我是ViewA";
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.Parameters.Keys.Contains("Text"))
Text = navigationContext.Parameters.GetValue<string>("Text");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
要传递参数的控件,在导航调转时把参数以键值对的形式传进去
NavigationParameters parameters = new NavigationParameters();
parameters.Add("Text", "我是参数传进来的");
regionManager.Regions["ContentRegion"].RequestNavigate(command, parameters);
2
3
这样就可以完成导航参数的传递了。
# b.导航拦截
ViewModel实现IConfirmNavigationRequest接口
public class ViewAViewModel : BindableBase, IConfirmNavigationRequest
{
private string text = "我是ViewA";
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
bool navigationResult = true;
if (MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
{
navigationResult = false;
}
continuationCallback(navigationResult);
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
if (navigationContext.Parameters.Keys.Contains("Text"))
Text = navigationContext.Parameters.GetValue<string>("Text");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

# c.导航日志
public class MainWindowViewModel : BindableBase
{
private string title = "PrismDemo";
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
private readonly IRegionManager regionManager;
private IRegionNavigationJournal regionNavigationJournal;
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = regionManager;
ButtonDelegateCommand = new DelegateCommand<string>(ButtonCommand);
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public void ButtonCommand(string command)
{
switch (command)
{
case "向前":
GoForward();
break;
case "向后":
GoBack();
break;
default:
NavigationParameters parameters = new NavigationParameters();
parameters.Add("Text", "我是参数传进来的");
regionManager.Regions["ContentRegion"].RequestNavigate(command, (callback) =>
{
regionNavigationJournal = callback.Context.NavigationService.Journal;
}, parameters);
break;
}
Title = command;
}
private void GoBack()
{
if (regionNavigationJournal.CanGoBack)
regionNavigationJournal.GoBack();
}
private void GoForward()
{
if (regionNavigationJournal.CanGoForward)
regionNavigationJournal.GoForward();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# 对话服务
在 Prism 框架中,对话服务(Dialog Service) 主要用于在应用程序中弹出对话框(对话框通常是指带有一些交互界面的窗口,比如模态窗口、提示框、确认框等),用于显示信息、获取用户输入或者处理复杂的交互。它是基于 MVVM(Model-View-ViewModel) 模式的一个关键组件,允许你将对话框的管理和视图模型解耦,从而提升代码的可测试性和可维护性。 被打开的窗口要实现IDialogAware接口,并实现抽象方法,可以进行参数的传入传出
public class ViewBViewModel : BindableBase, IDialogAware
{
private string text = "我是ViewB";
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public DialogCloseListener RequestClose { get; }
public ViewBViewModel()
{
ButtonDelegateCommand = new DelegateCommand<string>(ButtonCommand);
}
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
////向外边传参时不能用这个
//DialogParameters dialogParameters = new DialogParameters();
//dialogParameters.Add("UserName", "zhangsan");
//dialogParameters.Add("PWD", "123456");
////返回模态框操作状态及参数
//RequestClose.Invoke(dialogParameters, ButtonResult.OK);
}
public void OnDialogOpened(IDialogParameters parameters)
{
//加载传入来的参数
Text = parameters.GetValue<string>("入参");
}
private void ButtonCommand(string command)
{
switch (command)
{
case "确认":
Confrim();
break;
case "取消":
Close();
break;
default:
break;
}
}
private void Close()
{
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("UserName", "zhangsan");
dialogParameters.Add("PWD", "123456");
//返回模态框操作状态及参数
RequestClose.Invoke(dialogParameters, ButtonResult.OK);
}
private void Confrim()
{
DialogParameters dialogParameters = new DialogParameters();
dialogParameters.Add("UserName", "lisi");
dialogParameters.Add("PWD", "166656");
//返回模态框操作状态及参数
RequestClose.Invoke(dialogParameters, ButtonResult.OK);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<UserControl x:Class="PrismDemo.Views.ViewB"
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:PrismDemo.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewmodel="clr-namespace:PrismDemo.ViewModels"
d:DataContext="{d:DesignInstance Type =viewmodel:ViewBViewModel}">
<StackPanel Background="Yellow">
<TextBlock Text="{Binding Text}" FontSize="60"/>
<Button Content="确认" Command="{Binding ButtonDelegateCommand}" CommandParameter="确认" Width="40" Height="20" Margin="10"/>
<Button Content="取消" Command="{Binding ButtonDelegateCommand}" CommandParameter="取消" Width="40" Height="20" Margin="10"/>
</StackPanel>
</UserControl>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
打开窗口的调用方通过IOC容器拿到IDialogService
public class MainWindowViewModel : BindableBase
{
private string title = "PrismDemo";
public string Title
{
get { return title; }
set { SetProperty(ref title, value); }
}
private readonly IRegionManager regionManager;
private IRegionNavigationJournal regionNavigationJournal;
private IDialogService dialogService;
public MainWindowViewModel(IRegionManager regionManager, IDialogService dialogService)
{
this.regionManager = regionManager;
this.dialogService = dialogService;
ButtonDelegateCommand = new DelegateCommand<string>(ButtonCommand);
}
public DelegateCommand<string> ButtonDelegateCommand { get; set; }
public void ButtonCommand(string command)
{
switch (command)
{
case "向前":
GoForward();
break;
case "向后":
GoBack();
break;
case "打开窗口":
OpenDialog();
break;
default:
NavigationParameters parameters = new NavigationParameters();
parameters.Add("Text", "我是参数传进来的");
regionManager.Regions["ContentRegion"].RequestNavigate(command, (callback) =>
{
if(null != callback.Context)
regionNavigationJournal = callback.Context.NavigationService.Journal;
}, parameters);
break;
}
Title = command;
}
private void GoBack()
{
if (regionNavigationJournal.CanGoBack)
regionNavigationJournal.GoBack();
}
private void GoForward()
{
if (regionNavigationJournal.CanGoForward)
regionNavigationJournal.GoForward();
}
private void OpenDialog()
{
DialogParameters parameters = new DialogParameters();
parameters.Add("入参", "123");
dialogService.ShowDialog("ViewB", parameters, r =>
{
if (r.Result == ButtonResult.None)
Title = "Result is None";
else if (r.Result == ButtonResult.OK)
{
Title = "Result is OK";
var userName = r.Parameters["UserName"];
var PWD = r.Parameters["PWD"];
}
else if (r.Result == ButtonResult.Cancel)
Title = "Result is Cancel";
else
Title = "I Don't know what you did!?";
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

# 事件聚合器
在 Prism 中,事件聚合器(EventAggregator) 是实现 发布-订阅模式(Publish-Subscribe Pattern)的核心组件之一,它用于在应用程序的不同部分之间传递消息或事件。它解耦了事件的发布者和订阅者,使得它们不需要直接引用对方,从而实现更好的松耦合。
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
public class UserMessageEvent : PubSubEvent<User> { }
2
3
4
5
6
7
发布事件时传递一个 User 对象:
public class PublisherViewModel
{
private readonly IEventAggregator _eventAggregator;
public PublisherViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public void PublishUser()
{
var user = new User { Name = "Alice", Age = 25 };
_eventAggregator.GetEvent<UserMessageEvent>().Publish(user);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
订阅时,处理传递的 User 对象:
public class SubscriberViewModel
{
private readonly IEventAggregator _eventAggregator;
public SubscriberViewModel(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
_eventAggregator.GetEvent<UserMessageEvent>().Subscribe(OnUserReceived);
}
private void OnUserReceived(User user)
{
Console.WriteLine($"Received user: {user.Name}, Age: {user.Age}");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
一次性订阅(Once):通过使用 Subscribe 方法的重载,你可以让事件订阅者仅在第一次触发时执行处理逻辑,之后不再处理。EventAggregator 支持线程选项,指定事件处理是在 UI 线程还是后台线程执行。比如你希望在 UI 线程中处理事件,可以指定 ThreadOption.UIThread。
eventAggregator.GetEvent<MyMessageEvent>().Subscribe(OnMessageReceived, ThreadOption.UIThread, false);
# 模块化
# a.新建模块

添加ModuleAModule类,实现IModule接口,并且注册你想要的视图
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewC>();
}
}
2
3
4
5
6
7
8
9
10
11
这样模块就制作完毕。
# b.调用模块

在主程序中重写CreateModuleCatalog方法,这个是通过特定目录下加载模块的,也就是将你模块编译好的dll放在该目录下即可
public partial class App : PrismApplication
{
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>("ViewA");
containerRegistry.RegisterForNavigation<ViewB>("ViewB");
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
如果报错,可能是主项目与模块引用的Prism版本不同导致。