基于MVVM设计模式的Prism框架
一、前言
Prism 是一个用于构建富客户端桌面(WPF)和移动(Xamarin Forms)应用程序的框架,由微软提供。它基于微软的模式与实践(Microsoft Patterns & Practices)团队开发,并且是开源的。Prism 旨在简化应用程序开发过程,特别是那些遵循 MVVM(Model-View-ViewModel)模式的应用程序。
Prism框架的主要特点有模块化、MVVM 支持、依赖注入、导航、事件聚合器、命令、行为、服务定位器、视图模型定位器、区域和布局、配置和初始化等。
1、模块化:Prism 支持模块化应用程序设计,允许开发者将应用程序拆分成多个模块,每个模块包含自己的功能和视图。
2、MVVM 支持:Prism 深度集成了 MVVM 模式,提供了数据绑定、命令、事件聚合器等支持,简化了 ViewModel 和 View 之间的交互。
3、依赖注入:Prism 与依赖注入(DI)容器紧密集成,方便管理应用程序中的依赖关系和生命周期。
4、导航:Prism 提供了一个强大的导航服务,允许开发者定义和执行导航流程,支持参数传递和导航历史记录。
5、事件聚合器:Prism 提供了一个事件聚合器,简化了发布-订阅模式的实现,方便组件之间的松耦合通信。
6、命令:Prism 提供了多种命令实现,如 DelegateCommand 和 RelayCommand,简化了 ViewModel 中命令的创建和使用。
7、行为:Prism 引入了行为(Behaviors)的概念,允许开发者在不修改 ViewModel 的情况下,为 View 添加附加行为。
8、服务定位器:Prism 提供了一个服务定位器模式的实现,方便在应用程序中查找和使用服务。
9、视图模型定位器:Prism 提供了视图模型定位器,简化了视图模型的实例化和注册过程。
10、区域和布局:在 WPF 中,Prism 支持区域(Regions)的概念,允许开发者定义动态内容区域,可以在运行时加载和显示不同的视图。
11、配置和初始化:Prism 提供了配置和初始化模块和服务的机制,使得应用程序的启动和配置更加灵活。
以下是深入了解Prism源码或者使用包的地址。
GitHub:https://github.com/PrismLibrary/Prism
NuGet:https://www.nuget.org/packges/Prism.Wpf
Visual Studio Extension:Prism Template Pack
此外,附加一张Prism初始化的顺序流程图:
二、区域(Region)
区域是 Prism 中实现用户界面组合的一个关键概念。它允许开发者定义应用程序界面中的功能区域,并在运行时动态地向这些区域添加内容。区域可以包含在 ContentControl、ItemsControl 和 TabControl 等控件中 。
1、定义区域(Region)的几种写法
定义区域(Region)的方法主要有两种,一种是在XAML界面指定(RegionManager.RegionName),另一种是在代码(Code)中指定(RegionManager.SetRegionName)。
在实现界面指定或者代码指定之前我们需要先在App.xaml.cs类中创建启动界面,并在MainWindowViewModel.cs类(ViewModel)中实现新区域(ViewA界面)的指定,以下是对应的cs代码。
// App.xaml.cs类 创建界面方法
private override Window CreateShell()
{
// Prism框架中使用官方提供的容器实现界面的创建
return Container.Resolver<MainWidnow>();
}
// MainWindowViewModel.cs类(ViewModel) 构造函数
private readonly IRegionManager regionManager;
public MainWindowViewModel(IRegionManager regionManager)
{
this.regionManager = RegionManager;
regionManager.RegisterViewWithRegion("ContentRegion",typeof(ViewA));
}
①XAML界面指定(RegionManager.RegionName)
XAML代码
<!-- MainWindow界面(View) -->
<Grid>
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</Grid>
在以上例子中,我们在MainWindow界面创建了一个简单的区域(RegionManager)并在cs代码中将注册的这个区域绑定到一个新的界面上(ViewA),实现界面的展示。
②代码中指定(RegionManager.SetRegionName)
XAML代码
<!-- MainWindow界面(View) -->
<Grid>
<ContentControl x:Name:"ctr" />
</Grid>
C#代码
// MainWindow.cs类(View) 构造函数
public MainWindow()
{
InitializeComponent();
regionManager.SetRegionName(ctr, "ContentRegion");
}
在以上例子中,界面上通过对区域的命名(ctr),cs代码中通过Name属性找到对应的控件,并通过SetRegionName的方式实现区域的绑定。
2、RegionManager
RegionManager 是 Prism 框架中的核心组件之一,它负责管理 WPF 应用程序中的区域(Region)。区域是应用程序界面中可以动态加载和显示内容的容器。
RegionManager主要的作用有定义区域(RegionManager.RegionName)、维护区域集合(regionManager.Regions)、提供对区域的访问(regionManager.Regions[“MyRegion”])、合成视图(RegisterViewWithRegion(“MyRegion”, typeof(MyView)))、区域导航(RequestNavigate(“MyRegion”, “MyView”)) 等。
以下是他们代码的写法示例。
①定义区域(RegionManager.RegionName)
<ContentControl prism:RegionManager.RegionName="MyRegion" />
在 XAML 中使用 RegionManager.RegionName 附加属性为控件指定区域名称
②维护区域集合(regionManager.Regions)
var regions = regionManager.Regions;
通过 RegionManager.Regions 属性可以访问所有区域的集合。
③提供对区域的访问(regionManager.Regions[“MyRegion”])
var region = regionManager.Regions["MyRegion"];
通过 RegionManager.Regions[regionName]索引器获取对特定名称区域的引用。
④合成视图(RegisterViewWithRegion(“MyRegion”, typeof(MyView)))
regionManager.RegisterViewWithRegion("MyRegion", typeof(MyView));
AutoPopulateRegionBehavior 自动填充行为,当附加到区域时,自动实例化并添加注册到该区域的视图类型。
⑤区域导航(RequestNavigate(“MyRegion”, “MyView”))
regionManager.RequestNavigate("MyRegion", "MyView");
通过调用 RequestNavigate 方法并传入区域名称和视图名称以触发导航。
3、RegionAdapter
RegionAdapter 是 Prism 框架中的一个概念,它的作用是将 WPF 控件适配为可以包含动态内容的区域(Region)。
以下是一个将StackPanel控件添加适配器的过程。
XAML代码
在这里插入代码片
C#代码
// App.xaml.cs类中添加一个注册区域适配器的方法
protected override void ConfigureRegionAdapterMappings(RegionAdapterMappings regionAdapterMappings)
{
base.ConfigureRegionAdapterMappings(regionAdapterMappings);
regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdater >);
}
// 创建一个StackPanelRegionAdapter.cs类
// 基于StackPanel的区域适配器
public class StackPanelRegionAdater : RegionAdaterBase<StackPanel>
{
public StackPanelRegionAdater(TRegionRehaviorFactory regionBehaviorFactory)
{
}
// 若检测到StackPanel中有新增元素则将新元素新增到StackPanel内
protected override void Adapt(IRegion region, StackPanel regionTarget)
{
region.Views.CollectionChange += (s, e) =>
{
if(e.Action == System.Collections.Specialized.NotifyCollectionChangeAction.Add)
{
foreach(FrameworkElement item in e.NewItems)
{
regionTarget.children.Add(item);
}
}
};
}
protected override IRegion CreateRegion()
{
return new Region();
}
}
以上示例中,在App.xaml.cs类中通过重写ConfigureRegionAdapterMappings方法,在方法内添加了对StackPanel适配的适配器,实现StackPanel也能适配区域的效果。
在我们创建的StackPanelRegionAdapter.cs类中,主要重写了Adapt,以实现具体的适配方法与效果,这里我们通过校验当前是否是添加命令,是添加命令的前提下对整个数据源进行遍历,并将这些新的元素添加到StackPanel内。
三、模块(Module)
在 Prism 框架中,模块(Module) 是一种用于构建大型应用程序的方式,它将应用程序拆分成更小、更易于管理的部分。每个模块包含一组相关的功能、视图、视图模型、服务和资源。模块化有助于降低应用程序的复杂性,提高可维护性和可扩展性。
1、为什么有模块
在一个解决方案中,通常有一个主项目,其中包含很多视图,如下图所示。
在此示例中,有很明显的嵌套关系,导致的程序之间的相互依赖。此等设计会导致项目有较高的耦合性,在Prism框架中,为了降低此等情况的耦合,出现了模块的概念。Prism框架中,将每个视图分开成多个单独的模块,设计图如下。
在这个图示中,一个解决方案下有一个主项目和多个模块项目,可以明显看出主项目和模块项目之间没有直接的联系,来降低了程序的耦合性。每个模块项目指向主项目的内容区域,实现主项目调用模块项目实现界面展示。如下图所示。
2、创建模块的方法
在Prism源码中可以看到,创建模块主要支持五种方式,分别是AppConfig(配置文件)、Code(代码)、Directory(字典)、LoadManual(手动加载)、XAML(界面)。
① AppConfig(配置文件)
配置文件创建模块的方法,要在配置文件添加上配置,如下。
<configuration>
<configurations>
<section name="modules" type="prism.Modularity.ModulesConfigurationsection, prism.Wpf" />
</configurations>
<startup>
</startup>
<modules>
<module assemblyFile="ModuleA.dll" moduleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleAModule" startupLoaded="True" />
</modules>
</configuration>
在以上代码中,先添加了一个prism框架的模块引用,然后在模块引用中指向了ModuleA.dll模块,并以ModuleA为加载启动项。
② Code(代码)
代码的方式创建模块则只需要在App.xaml.cs类中的方法内添加绑定,如下。
protected override void ConfigureModuleCatalog(IModduleCatalog moduleCatalog)
{
moduleCatalog.AddModule<ModuleA.ModuleAModule>();
}
以上代码中,通过重写ConfigureModuleCatalog方法,并在其内调用AddModule方法注入ModuleA模块,是实现模块创建。
③ Directory(字典)
字典的方式创建模块只需要在App.xaml.cs类中的方法重写,返回一个新的对象就行了,如下。
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = @"./Modules" };
}
以上代码中,方法返回一个新的对象,对象中传出Modules的地址,再由界面上区域的Name使得ModuleA中的ViewModel能够将该区域注册为显示ViewA,实现ViewA界面的显示。
④ LoadManual(手动加载)
手动加载主要是通过LoadManual方法,手动调用模块创建,如下。
XAML页面代码
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Top" Content="Load Module" Click="Button_Click" />
<ContentControl prism:RegionManager.RegionName="ContentRegion" />
</DockPanel>
C#代码
// App.xaml.cs类中的方法
// 注册模块
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
var moduleAType = typeof(ModuleAModule);
moduleCatalog.AddModule(new ModuleInfo()
{
ModuleName = moduleAType.Name,
ModuleType = moduleAType.AssemblyQualifiedName,
InitializationMode = InitializationMode.OnDemand
});
}
// MainWindow.xaml.cs类中的方法
// 手动加载模块
private void Button_Click(object sender, RoutedEventArgs e)
{
_moduleManager.LoadModule("ModuleAModule");
}
以上示例是通过界面的点击事件,触发代码手动调用模块ModuleAModule创建,实现了模块调用。此外,这里在方法手动加载之前需要先在App.xaml.cs类中将这个对象注册到实例中。
⑤ XAML(界面)
界面加载模块主要是通过新建一个界面来存储需要创建的模块的信息,再由配置文件进行注册,如下。
<!-- App.config -->
<configSections>
<section name="modules" type="Prism.Modularity.ModulesConfigurationSection, Prism.Wpf" />
</configSections>
<startup>
</startup>
<modules>
<module assemblyFile="ModuleA.dll" moduleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" moduleName="ModuleAModule" startupLoaded="True" />
</modules>
<!-- ModuleCatalog.xaml -->
<m:ModuleInfo ModuleName="ModuleAModule"
ModuleType="ModuleA.ModuleAModule, ModuleA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
以上代码中,先有App.config文件注册一个ModuleA.dll文件,并指向Name为ModuleAModule模块,再创建ModuleCatalog.xaml并存放该模块的信息,最终实现模块的创建。
3、视图注入
在 Prism 框架中,视图注入(View Injection)是一种技术,允许将视图(View)与模块(Module)动态地关联到应用程序的用户界面中。这种机制使得应用程序可以在运行时加载和显示来自不同模块的视图,而不需要在编译时静态地引用它们。
public class ModuleAModule : IModule
{
// 初始化时访问区域并对区域进行导航
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var region = regionManager.Regions["ContentRegion"];
region.RequestNavigate("ViewA");
}
// 注册视图
publi void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
以上代码示例中,先视图ViewA注册,再在初始化的时候先找到对应区域,然后对该区域进行导航到视图ViewA,实现了视图注入操作,可以动态的对视图进行注入操作。
四、MVVM
Prism框架作为MVVM设计模式下的一种框架,其中MVVM也有其他的框架,每个框架有不同的支持内容,以下是Prism与MVVM几个较为经典的框架的功能对比表。
功能 \ 通知 | Prism | Mvvmlighr | Micorosoft.Toolkit.Mvvm |
---|---|---|---|
通知 | BindadleBase | ViewModelBase | ObservableObject |
命令 | DelegateCommand | RelayCommand | Async/RelayCommand |
聚合器 | IEventAggregator | IMessenger | IMessenger |
模块化 | √ | × | × |
容器 | √ | × | × |
依赖注入 | √ | × | × |
导航 | √ | × | × |
对话 | √ | × | × |
1、绑定(BindableBase)
如果在此之前,用mvvmlight框架的话,ViewModel继承的类如下:
public class TestViewModel : ViewModelBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; RaisePropertyChanged(); }
}
}
在Prism当中,需要继承于BindableBase,如下所示:
public class TestViewModel : BindableBase
{
private string _message;
public string Message
{
get { return _message; }
set { _message = valule; RaisePropertyChanged(); }
}
}
2、命令
在mvvmlight/microsoft.toolkit.mvvm中,声明Command,如下:
public class TestViewModel : ViewModelBase
{
public RelayCommand SednCommand { get; set; }
public RelayCommand<string> SednMessageCommand { get; set; }
}
在Prism中,可以使用DelegateCommand及带参数的Command,如下:
public class TestViewModel : ViewModelBase
{
public DelegateCommand SednCommand { get; set; }
public DelegateCommand<string> SednMessageCommand { get; set; }
}
根据以上描述,在Prism框架下实现一个简单的绑定和命令的示例,如下:
XAML界面代码
<UserControl
...
Prism:ViewModelLocator.AutoWireViewModel="True"/>
<Grid>
<StackPanel>
<Button Command="{Binding OpenCommand}" Content="UpdateText" />
<TextBlock FontSize="38" Text="{Binding Title}" />
</StackPanel>
</Grid>
</UserControl>
C#代码
public class ViewAViewMOdel : BindableBase
{
public ViewAViewModel()
{
Title = "Hello!";
OpenCommand - new DelegateCommand(() =>
{
Title = "Prism";
});
}
private string _title
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged(); }
}
piblic DelegateCommand OpenCommand { get; private set; }
}
以上代码中,界面通过AutoWireViewModel置为True实现自动绑定,但是前提是View必须在同一目录下的Views文件夹内,ViewModel必须在同一目录下的ViewModels文件夹内。以上示例中通过界面点击触发OpenCommand事件,事件触发将界面上绑定的Title属性的值变更。
3、复合命令(CompositeCommand)
在 Prism 框架中,复合命令(Composite Command)是一种特殊类型的命令,它允许将多个 ICommand 对象组合成一个单一的命令。这样,当触发复合命令时,所有包含的命令都会被执行。这种命令模式非常有用,特别是在需要根据单一用户界面操作(如点击一个按钮)执行多个操作的场景中。
当界面触发了事件后,会去执行复合命令,然后复合命令通知各个单独的命令,实现命令的实现,如下图。
当其中有一个子命令不能执行的情况,复合命令也会无法执行,界面上也无法触发该复合命令,如下图。
public class ViewAViewMOdel : BindableBase
{
public ViewAViewModel()
{
Title = "Hello!";
OpenCommand1 - new DelegateCommand(() =>
{
Title += “OpenCommand1 \r\n”;
});
OpenCommand2 - new DelegateCommand(() =>
{
Title += “OpenCommand2 \r\n”;
});
// 将两个命令注册到复合命令内
OpenAll = new CompositeCommand();
OpenAll.RegisterCommand(OpenCommand1);
OpenAll.RegisterCommand(OpenCommand2);
}
private string _title
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged(); }
}
piblic DelegateCommand OpenCommand1 { get; private set; }
piblic DelegateCommand OpenCommand2 { get; private set; }
piblic DelegateCommand OpenAll { get; private set; }
}
在以上示例中,我们实现了OpenCommand1 和OpenCommand2事件,再将这两个事件注册到OpenAll事件内,实现触发OpenAll事件就同时依次触发了OpenCommand1和OpenCommand2事件,实现了将OpenCommand1和OpenCommand2事件复合到OpenAll事件的复合事件。
五、事件聚合器(Eevent Aggregator)
在 Prism 框架中,事件聚合器(Event Aggregator)是一个设计模式,用于实现发布-订阅(Publish-Subscribe)模式的松耦合通信机制。事件聚合器作为中央事件处理系统,允许对象间进行交互而不需要直接引用彼此,这有助于降低系统的耦合度并提高模块化。如图:
1、事件过滤器(IEventAggregator)
事件过滤器是用于控制发布事件的,可以通过一些参数对发布的信息进行控制,只发布过滤过后的信息。如图:
图例中过滤了Id等于1和3的事件发布,只有Id是2的时候才会发布信息
XAML界面代码
<Grid>
<StackPanel>
<Button Command="{Binding OpenCommand}" Content="订阅"/>
<Button Command="{Binding SendCommand}" Content="发布" />
<TextBlock FontSize="38" Text="{Binding Title}" />
</StackPanel>
</Grid>
C#代码
public class ViewAViewMOdel : BindableBase
{
public ViewAViewModel(IEventAggregator eventAggregator)
{
SubscribeCommand = new DelegateCommand(() =>
{
// 不需要过滤事件的情况
// eventAggregator.GetEvent<MessageEvent>().Subscribe(OnMessageReeived);
// 增加过滤器的情况
eventAggregator.GetEvent<MessageEvent>().Subscribe(OnMessageReeived, ThreadOption.PublisherThread, false, msg =>
{
if(msg.Equals("Hello"))
{
return true;
}
else
{
return false;
}
);
});
SendCommand = new DelegateCommand(() =>
{
eventAggregator.GetEvent<MessageEvent>().Publish("Hellow~~~~~");
});
this.eventAggregator = eventAggregator;
}
public void OnMessageReceived(string message)
{
Title += message + "\r\n";
}
private string _title;
private readonly IEventAggreator eventAggregator;
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged(); }
}
public DelegateCommand SubscribeCommand { get; private set; }
public DelegateCommand SendCommand { get; private set; }
}
public class MessageEvent : PubSubEvent<string>
{
}
以上代码示例中,订阅是增加了过滤机制,过滤后只显示公共线程的数据,且每次订阅完成后都会垃圾回收掉资源,且只有msg 是”Hello“的情况才会订阅。
2、订阅取消
除此之外,聚合器还提供了订阅取消的方法。
SendCommand = new DelegateCommand(() =>
{
eventAggregator.GetEvent<MessageEvent>().Unsubscribe(OnMessageReceived);
}
在以上示例中,当触发了发布事件后,将会取消先前的订阅事件,实现订阅的动态取消。
六、导航(Navigation)
在 Prism 框架中,导航是一个核心概念,它允许应用程序在运行时动态地加载和显示不同的视图(View)。这种机制使得用户界面可以更加灵活和动态,类似于单页面应用程序(SPA)的体验。
1、翻页
导航功能可以通过事件的方法触发,导航到新的视图处。
// App.xaml.cs
public override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>("PageA");
containerRegistry.RegisterForNavigation<ViewB>();
}
// MainWindowViewModel.cs
public MainWindowViewModel(IRegionManager regionManager)
{
OpenACommand = new DelegateCommand(OpenA);
OpenBCommand = new DelegateCommand(OpenB);
this.regionManager = regionManager;
}
public void OpenA()
{
regionManager.ReqyestNavigate("ContentRegion", "PageA");
}
public void OpenB()
{
regionManager.ReqyestNavigate("ContentRegion", "ViewA");
}
以上示例中,显示在App.xaml.cs类中注册了ViewA和ViewB视图,并将ViewA取别名为PageA,在后面MainWindowViewModel.cs类中,通过触发OpenACommand事件来调用OpenA方法来实现了PageA界面的跳转展示。
2、传参
在导航到新页面的时候可以由当前页的ViewModel给新页面传递部分参数,具体实现如下。
// MainWindowViewModel.cs
private void OpenA()
{
// 另一种传参方式
// regionManger.RequestNavigate("ContentRegion", $"PageA?Value=Hello");
NavigationParameters param = new NavigationParameters();
param.Add("Value", "Hello");
regionManager.RequestNavigate("ContentRegion", "PageA", param);
}
// ViewAViewModel.cs
public class ViewAViewModel : BindableBase, INavigationAware
{
private string _title;
public string Title
{
get { return _title; }
set { _title = value; RaisePropertyChanged(); }
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
Title = navigationContext.Parameters.GetValue<string>("Value");
}
}
以上示例中,通过在导航到ViewA视图前传入一个Vlaue值,来实现界面ViewA展示Value值的功能。在此情况下ViewA需要创建一个ViewAViewModel来执行这些逻辑控制,此类需要继承自INavigationAware以实现导航的功能,在重写的OnNavigatedTo中,将Title属性的值复制为传进来的Value 的值,最终在ViewA界面上实现对Value的展示。
3、跳转
在导航接口中,不仅实现了打开前传入值,还有跳转前的操作方法,具体实现如下。
public class ViewAViewModel : IConfirmNavigationRequest
{
public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
bool result = true;
if(MessageBox.Show("确认导航?", "温馨提示", MessageBoxButton.YesNo) == MessageBoxResult.No)
{
result = false;
}
continuationCallback(result);
}
// 省略另外三个需要实现的方法
}
在以上示例中,将接口从INavigationAware改为了IConfirmNavigationRequest,实现ConfirmNavigationRequest方法来实现了跳转前的提示,并在提示框弹出后控制是否跳转。
IConfirmNavigationRequest接口继承自INavigationAware并且多了一个ConfirmNavigationRequest方法,故IConfirmNavigationRequest是兼容INavigationAware的。
4、日志
导航日志可以实现上一页、下一页等功能。
如上图所示,可以看到D页面可以回到上一页,E页面可以回到下一页。
// MainWindowViewModel.cs
IregionNavigationJournal journal;
private void GoForward()
{
journal.GoForward();
}
private void GoBack()
{
journal.GoBack();
}
private void OpenA()
{
regionManager.RequestNavigate("ContentRegion", $"PageA?Value=Hello", arg =>
{
journal = arg.Context.NavigationService.Journal;
});
}
private void OpenB()
{
regionManager.RequestNavigate("ContentRegion", "ViewB", arg =>
{
journal = arg.Context.NavigationService.Journal;
});
}
以上示例中,打开ViewA和ViewB之前都记录了日志,后面可以通过GoForward和GoBack方法,实现界面的前进和返回功能。
七、对话(Dialog)
在 Prism 框架中,对话(Dialog) 服务是一种用于管理和显示对话框的机制。这个服务允许你在应用程序中以一种声明式和松耦合的方式来显示对话框,并处理用户输入和交互。
// App.xaml.cs
protected override void RegisterTypes(ICOntainerRegistry)
{
containerRegistry.RegisterDialog<MsgView, MsgViewModel>("Question");
}
// MsgViewModel.cs
public class MsgViewModel : BindalbleBase, IDialogAware
{
public MsgViewModel()
{
SaveCommand = new DelegateCommand(() =>
{
DialogParameters param = new DialogParameters();
param.Add("Value", Title);
RequestClose?.Invoke(new DialogResult(ButtonResult.OK, param));
});
CancelCommand = new DelegateCommand(() =>
{
RequestClose?.Invoke(new DialogResult(ButtonResult.No));
});
}
public DelegateCommand SaveCommand { get; set; }
public DelegateCommand CancelCommand { get; set; }
private string _title;
public string Title
{
get { return _title; }
set {_title = value; }
}
public event Action<IDialogResult> RequestClose;
public bool CanCloseDialog()
{
return true;
}
public void OnDialogClosed()
{
}
public void OnDialogOpened(IDialogParameters parameters)
{
var title = parameter.GetValue<string>("Value");
}
}
// MainWindowViewModel.cs
private void OpenA()
{
DialogParameters param = new DialogParameters();
param.Add("Value", "Test1");
dialog.ShowDialog("Question", param, arg =>
{
if(arg.Result == ButtonResult.OK)
{
var vaule = arg.Parameters.GetValue<string>("Value");
MessageBox.Show($"用户输入了:{value}");
}
else
{
MessageBox.Show("用户取消了弹窗");
}
});
}
以上示例中,实现对话需要先在App.xaml.cs里面注册一个对话框,并新建一个对话的ViewModel,实现对话逻辑,MsgViewModel类中主要是实现了保存和取消两个命令,保存命令实现将Title属性值取出并存入到param的Value属性里面,取消命令实现了弹窗的关闭。
主要通过OpenA中调用这个对话逻辑,当界面打开时,初始的Value赋值为Test1,即打开界面显示Test1,当输入内容后点击确认将弹窗提示输入内容,当取消或者关闭弹窗时将弹出取消弹窗的提示弹窗。
八、总结
Prism 是一个用于构建富客户端桌面和移动应用程序的框架,特别是在 .NET 平台上。它基于微软的模式与实践团队开发,并且是开源的。Prism 旨在简化应用程序的开发,特别是那些遵循 MVVM(Model-View-ViewModel)模式的应用程序。Prism 的目标是帮助开发者构建灵活、可维护和易于测试的应用程序。通过提供一套丰富的工具和模式,Prism 使得开发复杂的用户界面和业务逻辑变得更加简单和高效。