WPF Prims框架详解

文章目录

前言

Prims框架是WPF的一个框架,特点是集成了我们平常常用的开发模式,封装了很多常用的功能。例如消息通知,路由导航,Model绑定。

Prism基本使用

资料来源

WPF-Prism8.0核心教程(公益)

Prism选择,DryIoc还是Unity

根据网上的说明,DryIoc是更加效率更高的,但是我看Nuget下载数量里面,Unity下载数量比DryIoc高得多。具体没怎么用过,那就按照推荐用DryIoc好了。

Unity和DryIoc之间性能比较

Prism基本框架搭建

在App.xaml里面,修改

App.xaml.cs

java 复制代码
    /// <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

html 复制代码
<!--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中添加入如下代码
html 复制代码
xmlns:prism引入命名空间
prism:ViewModel设置自动绑定
可以不加,默认进行了绑定。
<Window ...
        xmlns:prism="http://prismlibrary.com/"
        prism:ViewModelLocator.AutoWireViewModel="True">

也可以在App.xaml里面强行绑定

在原文件件中取消绑定

app.xaml

html 复制代码
        /// <summary>
        /// 路由管理
        /// </summary>
        /// <param name="containerRegistry"></param>
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            //通过RegisterForNavigation进行强行绑定,强行让ViewA和ViewAViewModel进行绑定
            containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
            
        }

参数动态更新

java 复制代码
	//继承BindableBase接口
    public class MyListBoxItem : BindableBase
    {
        private string text;
        public string Text
        {
            get { return text; }
            set {  SetProperty(ref text, value);}//此方法为Prism提供
        }
    }

可以输入propp加tab键,模板输入,提交写代码效率

函数动态绑定

函数动态绑定需要用到委托类型,如果委托不明白需要自己回去了解一下

viewModel

java 复制代码
//声明委托
public DelegateCommand TestBtn { get; set; }
//构造函数
public MainWindowViewModel()
{
    //实例化委托
    TestBtn = new DelegateCommand(() =>
    {
        //执行内容
    });
}

view

html 复制代码
使用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

html 复制代码
    <Grid>
        <!--声明Region,名称为ContentRegion,使用名称来进行注入-->
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        
    </Grid>

UserControl1.xaml,用于放置在Region上面的用户控件

html 复制代码
    <Grid>
        <TextBlock  Text="我是用户控件" FontSize="100"/>
    </Grid>

MainWindowViewModel.xaml

java 复制代码
        //注册regionManager控件
        private readonly IRegionManager _regionManager;

        public MainWindowViewModel(IRegionManager regionManager)
        {
            this._regionManager = regionManager;
			//ContentRegion是窗口已声明的Region,使用字符串进行弱绑定
            regionManager.RegisterViewWithRegion("ContentRegion",typeof(UserControl1));
        }

生成效果

测试是否限制空间

html 复制代码
<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <!--声明Region,名称为ContentRegion,使用名称来进行注入-->
        <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        
    </Grid>

实现效果

消息订阅

消息订阅是先订阅再发布有效,如果是先发布再订阅则无效。

如何使用消息订阅

java 复制代码
 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)

取消订阅

java 复制代码
eventAggregator.GetEvent<MessageEvent>().Unsubscribe(OnMessageRecevied);

使用建议

  • 注意,这个是全局使用的,所以一般用于页面通讯
    • 例如:AView。我就用AViewEvent。一个页面对应一个事件通知。
  • 由于带入的参数不一定,所以我们可以设置Dictionary为入参,通过key值过滤来设置对应的函数。通过NetJson来快速传参。

路由导航

路由导航和Region是搭配使用的,专门用于页面转化

  • 新建切换用的用户控件ViewA,ViewB,ViewC。ViewA,B,C内容有区分,这里省略
  • 在App.xaml.cs中注册页面
java 复制代码
        /// <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中添加委托事件
html 复制代码
   <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里面管理导航

java 复制代码
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中进行路由拦截

java 复制代码
 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>("别名");
java 复制代码
_regionManager.RequestNavigate("ContentRegion", "别名");//原本是ViewA的,可以替换成别名

对话框/弹窗功能

Prism将对话框这个常用的功能进行了封装。虽然叫对话框,但其实是弹窗的功能效果。

实现代码

app.xaml中

java 复制代码
        /// <summary>
        /// 路由管理
        /// </summary>
        /// <param name="containerRegistry"></param>
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
			.......
            //这里也能起别名
            containerRegistry.RegisterDialog<MsgView,MsgViewModel>("别名");   
        }

MainViewModel使用这个弹窗

java 复制代码
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

html 复制代码
    <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>
java 复制代码
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");
        }
    }
相关推荐
Elastic 中国社区官方博客7 小时前
如何将数据从 AWS S3 导入到 Elastic Cloud - 第 3 部分:Elastic S3 连接器
大数据·elasticsearch·搜索引擎·云计算·全文检索·可用性测试·aws
晨欣12 小时前
Elasticsearch和Lucene之间是什么关系?(ChatGPT回答)
elasticsearch·chatgpt·lucene
极梦网络无忧16 小时前
Unity中IK动画与布偶死亡动画切换的实现
unity·游戏引擎·lucene
月落.16 小时前
WPF的<ContentControl>控件
wpf
就是有点傻16 小时前
WPF中的依赖属性
开发语言·wpf
wangnaisheng16 小时前
【WPF】把一个Window放在左上角/右上角顶格显示
wpf
WineMonk16 小时前
.NET WPF CommunityToolkit.Mvvm框架
.net·wpf·mvvm
月落.16 小时前
WPF中的INotifyPropertyChanged接口
wpf
界面开发小八哥16 小时前
界面控件DevExpress WPF中文教程:Data Grid——卡片视图设置
.net·wpf·界面控件·devexpress·ui开发
平凡シンプル16 小时前
WPF 打包
wpf