zoukankan      html  css  js  c++  java
  • 四、从GitHub浏览Prism示例代码的方式入门WPF下的Prism之Mvvm的13示例

    上一篇之分析了示例,没有最终写DEMO,把这一篇分析完,总结后一起写Prism下的MVVM例子。

    这一篇开始分析从13示例开始,分析到MVVM主要部分结束然后写一个分析后的总结DEMO

    添加一段新的内容:Prism中新的内容还是挺多的,之前的思路是一篇里面写好几个Prism的例子,过一遍示例的代码,过完所有的Prism也就学完了。结果到第13篇的时候,卡住我了,2天才解决完这一个示例,而且发现其实只是Prism的一个特性,这个Prism还是要慢慢学,不要着急。有可能他新的一个接口,只有很少的代码,但是实际上实际干了很多的事情。比如这一篇里实际就是在讲IActiveAware接口。关键参数只有2个。但是整整2天我才搞明白是怎么回事。最近工作上、家庭上的事情比较多,感觉太累了,但是我会调整好状态,坚持下去。

    这一篇示例主要是分析IActiveAware;

    从13示例继续学习Prism下的MVVM思想

    分析13UsingCompositeCommands示例

    1、引用关系

    UsingCompositeCommands包含3个工程

    1.1、ModuleA工程引用了Prism.Wpf包、UsingCompositeCommands.Core;

    1.2、UsingCompositeCommands工程引用了Prism.Unity包、ModuleA项目、UsingCompositeCommands.Core

    1.3、UsingCompositeCommands.Core引用了Prism.Core包

    我们从引用关系最小的开始看,UsingCompositeCommands.Core

    2、分析UsingCompositeCommands.Core工程

    2.1、新增ApplicationComands.cs

    创建了ApplicationCommands接口,包含了一个属性SaveCommand;

    添加类,并继承自IApplicationCommands接口

     public class ApplicationCommands : IApplicationCommands
        {
            private CompositeCommand _saveCommand = new CompositeCommand(true);
            public CompositeCommand SaveCommand
            {
                get { return _saveCommand; }
            }
        }
    

    这里和12例子有一个明显的差别,在初始化_SaveCommand的时候new CompositeCommand(true),传入了True。这里比较重要, 这里使用F12可以看到参数monitorCommandActivity,传入这个参数为True时,CompositeCommand类将会进行以下行为:

    • CanExecute。只有当所有的活动命令可以被执行时,才会返回true。非活动的命令将不会执行。
    • Execute。执行处于活动状态的命令,非活动的命令不会执行。

    通过在ViewModel中实现IActiveAware接口,在Region中的子View变成活动窗口或者非活动窗口时都会被通知。当子View状态改变时,只有当前处于活动状态的View下的ViewModel的Command才会被执行。

    3、分析主工程UsingCompositeCommands

    引用了Prism.Unity包;

    引用了ModuleA;

    引用了UsingCompositeCommands.Core;

    3.1、App.xaml

    添加命名空间:xmlns:prism="http://prismlibrary.com/";

    修改Appication为prism:PrismApplication;

    去掉StartupUri属性;

    3.2、App.cs

    重写CreateShell()方法设置启动窗体

    重写RegisterTypes()

    使用容器注入UsingCompositeCommands.Core中的IApplicationCommands,以便在ViewModel中使用;

    重写ConfigureModuleCatalog()

    使用代码加载ModuleA项目中的ModuleAModule;

    3.3、Views下的MainWindow.xaml

    关键部分:

    添加命名空间xmlns:prism="http://prismlibrary.com/"

    设置prism.ViewModelLocator.AutoWireViewModel=Ture

    添加了TabControl控件,并设置了附加依赖项属性RegionName,用于设置关联View的显示区域。

    添加了Button按钮,设置了Command为ViewModel下的ApplicationCommands.SaveCommand,ApplicationCommands是App.cs在启动是重写RegisterTypes()时注册的。cs中无新增代码

    3.4、ViewModel下的MainWindowViewModel.cs

    MaindowViewModel继承自BindableBase

    创建了IApplicationCommands属性并构造函数中初始化IApplicationCommands,

    4、分析ModuleA工程

    ModuleA工程引用Prism.Wpf包;

    引用UsingComoisuteCommands.Core;

    4.1、ModuleAModule

    ModuleAModule继承自IModule,

    在OInitialized方法中,使用containerProvider关联了Region和View。用于在显示区域添加View

    4.2、ViewModels下的TabViewModel.cs

    TabViewModel继承自BindableBase和IActiveAware。

    就是这个IActiveAware卡了我两天。

    其他的地方不讲,跟12示例一样,就讲13示例里不一样的。

    看属性IsActive。在Set时触发了OnIsActiveChanged()

    根据调试时的情况。当只点击一个TabView的时候,IsActive只触发一次,第二次点击的时候,有2个IsActive进入两次,点击第三个的时候有3个。

     bool _isActive;
            public bool IsActive
            {
                get { return _isActive; }
                set
                {
                    _isActive = value;
                    OnIsActiveChanged();
                }
            }
            private void OnIsActiveChanged()
            {
                UpdateCommand.IsActive = IsActive;
    
                IsActiveChanged?.Invoke(this, new EventArgs());
            }
    

    一直没搞明白后来F12跳转到了IActiveAware才明白,这个是用来控制活动View和ViewModel下的Command的。而决定这个是否工作的就是在UsingCompositeCommands.Core下的ApplicationCommands 在初始化_saveCommand字段时的true参数

    private CompositeCommand _saveCommand = new CompositeCommand(true);
    

    我在看明白后,尝试设置true为false,发现所有的View的Command都会执行了,

    执行主工程下的Save时,3个TabView都会更新。而值为true的时候,只有处于激活状态的TabViewModel才会更新。

    结合上一篇的内容我们写一个DEMO

    打开12、13示例,我们先回忆一遍12、13示例在干什么。我们从逻辑关联最少的开始回忆道逻辑关联最多的。这里希望我们自己去回忆,想不起来了,在去看。

    1.1 两个示例的UsingCompositeCommands.Core都引用了Prism.Core;

    创建了IApplicationCommands和ApplicationCommands;

    添加了2个CompsiteCommand,一个是不带参数的,一个是带参数true的。这个是配合Modules下的IactivateAware接口使用的,用于是是否只更新活动状态下的ViewModel的内容。Prism.Core的内容就结束了

    1.2 两个UsingCompositeCommands.Modues都引用了Prism.Wpf和UsingCompositeCommands.Core;

    ModuleAModule都继承自ImOdule 并重写了OnInitialized方法,在里面完成了Region跟View的关联,和初始化。Views和ViewModels通过在xaml中编写Prism.ViewModelLocator.AutoWrieViewModel=true实现自动关联,在ViewModel中通过构造函数传入了IApplicationCommands接口,创建_applicationCommands对象绑定并注册对应的事件,用于执行全局的Command。

    1.3主工程UsingCompsiteCommands引用了Prism.Unity、ModuleA和UsingCompositeCommands.Core;

    重写App为PrismApplication,重写CreteShell()方法,设置启动对象。

    重写RegisterTypes()加载UsingCompsiteCommands.Core下的ApplicationCommands

    重写ConfigureModuleCatalog()用于加载Module。

    ViewModel中创建属性IApplicationCommands并在构造函数中初始化。

    View的XAML中使用Prism.ViewModelLocator.AutoWireViewModel=true关联ViewModel

    View的XAML中直接设置Command为自己ViewModel下的ApplicationCommands的方法,用于关联。

    同时设置显示区域控件并设置Region属性,用于在Modules下关联显示内容。

    回忆完之后,在继续向下看;如果没有,建议在回忆一遍,接下来我们结合12 13示例,开始写DEMO代码;

    我们结合12、13示例,创建一个有多个TabControl的显示页面,编写2个全局按钮功能,一个是全局的AllSave方法触发时所有页面更新、一个全局的CurrentSave方法触发时当前页面更新,子页面包含两个Textblock和一个Button,Textblock用于显示标题和当前时间,button用于触发更能当前时间。用于熟悉Command和IActiveAware实现不同的Command的逻辑执行。

    PrismMvvmDemo.Core

    引用Prism.Core、添加IApplicationCommands接口,编写2个保存的Command

    using Prism.Commands;
    
    namespace PrismMvvmDemo.Core
    {
        public interface IApplicationCommands
        {
            CompositeCommand AllSave { get; }
            CompositeCommand CurrentSave { get; }
        }
        public class ApplicationCommands:IApplicationCommands
        {
            private CompositeCommand _allSave = new CompositeCommand();
            public CompositeCommand AllSave
            {
                get
                {
                    return _allSave;
                }
            }
    
            private CompositeCommand _currentSave = new CompositeCommand(true);
            public CompositeCommand CurrentSave
            {
                get
                {
                    return _currentSave;
                }
            }
        }
    }
    
    

    PrismMvvmDemo.Modules

    引用了Prism.Wpf、PrismMvvmDemo.Core

    添加ModuleAModule 并继承自IModule,并在OnInitialized()方法中关联region和View。

    ModuleAModule.cs的代码

    添加3个TabView设置Title并关联到TabViewControlRegion显示区域。代码如下:还没有添加View和ViewModels,显示区域TabControlRegion也没有添加。

    using Prism.Ioc;
    using Prism.Modularity;
    using Prism.Regions;
    using PrismMvvmDemo.Modules.ViewModels;
    using PrismMvvmDemo.Modules.Views;
    
    namespace PrismMvvmDemo.Modules
    {
        public class ModuleAModule : IModule
        {
            public void OnInitialized(IContainerProvider containerProvider)
            {
                var regionManager = containerProvider.Resolve<IRegionManager>();
                var region = regionManager.Regions["TabControlRegion"];
                var tabViewA = containerProvider.Resolve<TabView>();
                SetTitle("TabViewA", tabViewA);
                region.Add(tabViewA);
                var tabViewB = containerProvider.Resolve<TabView>();
                SetTitle("TabViewB", tabViewB);
                region.Add(tabViewB);
                var tabViewC = containerProvider.Resolve<TabView>();
                SetTitle("TabViewC", tabViewC);
                region.Add(tabViewC);
    
            }
    
            public void RegisterTypes(IContainerRegistry containerRegistry)
            {
    
            }
    
            private void SetTitle(string title, TabView tabView)
            {
                (tabView.DataContext as TabViewModel).Title = title;
            }
    
        }
    }
    
    

    再Modules下添加Views目录和ViewModels目录

    Views下添加自定义控件TabView.xaml,主要设置Prism:ViewModelLocator.AutoWireViewModel=true

    然后绑定CurrentTime、Binding UpdateTimeCommand。

    <UserControl x:Class="PrismMvvmDemo.Modules.Views.TabView"
                 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:PrismMvvmDemo.Modules.Views"
                 xmlns:prism="http://prismlibrary.com/"
                 prism:ViewModelLocator.AutoWireViewModel="True"
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
        <StackPanel>
            <TextBlock Text="当前时间:"/>
            <TextBlock  Text="{Binding CurrentTime}"/>
            <Button Width="120" Height="30" Content="单击更新时间" Command="{Binding UpdateTimeCommand}"/>
        </StackPanel>
    </UserControl>
    
    

    ViewModels下TabViewModel.cs代码,主要是继承自BindableBase和IActiveAware。通过构造函数绑定了ApplicationCommands并关联到了ViewModel下的Command。

    using Prism;
    using Prism.Commands;
    using Prism.Mvvm;
    using PrismMvvmDemo.Core;
    using System;
    
    namespace PrismMvvmDemo.Modules.ViewModels
    {
        public class TabViewModel : BindableBase, IActiveAware
        {
            IApplicationCommands _applicationCommands;
    
            private string _title;
            public string Title
            {
                get { return _title; }
                set
                {
                    SetProperty(ref _title, value);
                }
            }
            private bool _canUpdate = true;
            public bool CanUpdate
            {
                get { return _canUpdate; }
                set { SetProperty(ref _canUpdate, value); }
            }
    
            private string _currentTime = string.Empty;
    
    
            public string CurrentTime
            {
                get { return _currentTime; }
                set { SetProperty(ref _currentTime, value); }
            }
            public TabViewModel(IApplicationCommands applicationCommands)
            {
                _applicationCommands = applicationCommands;
                UpdateTimeCommand = new DelegateCommand(UpdateTime).ObservesCanExecute(() => CanUpdate);
                _applicationCommands.CurrentSave.RegisterCommand(UpdateTimeCommand);
                _applicationCommands.AllSave.RegisterCommand(UpdateTimeCommand);
            }
    
            private void UpdateTime()
            {
                CurrentTime = $"Update Time {DateTime.Now}";
            }
    
            public event EventHandler IsActiveChanged;
            public DelegateCommand UpdateTimeCommand { get; private set; }
            private bool _isActive;
            public bool IsActive
            {
                get { return _isActive; }
                set
                {
                    _isActive = value;
                    OnIsActiveChanged();
                }
            }
    
            private void OnIsActiveChanged()
            {
                UpdateTimeCommand.IsActive = IsActive;
                IsActiveChanged?.Invoke(this, new EventArgs());
            }
        }
    }
    
    

    PrismMvvmDemo.Runner 主工程

    添加了Prism.Unity的库,添加了PrismMvvmDemo.Core和PrismMvvmDemo.Modules两个库。

    重写App.xaml 注意引用using Prism.Ioc;

    <prism:PrismApplication x:Class="PrismMvvmDemo.Runner.App"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:PrismMvvmDemo.Runner"
                 xmlns:prism="http://prismlibrary.com/">
        <Application.Resources>
    
        </Application.Resources>
    </prism:PrismApplication>
    
    
    using Prism.Unity;
    using Prism.Ioc;
    using System.Windows;
    using Prism.Modularity;
    using PrismMvvmDemo.Core;
    using PrismMvvmDemo.Runner.Views;
    
    namespace PrismMvvmDemo.Runner
    {
        /// <summary>
        /// App.xaml 的交互逻辑
        /// </summary>
        public partial class App : PrismApplication
        {
            protected override Window CreateShell()
            {
                return Container.Resolve<MainWindow>();
            }
            protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
            {
                base.ConfigureModuleCatalog(moduleCatalog);
                moduleCatalog.AddModule<Modules.ModuleAModule>();
            }
            protected override void RegisterTypes(IContainerRegistry containerRegistry)
            { 
                containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>();
            }
        }
    }
    
    

    ViewModel创建MainWindowViewModel代码

    using Prism.Mvvm;
    using PrismMvvmDemo.Core;
    
    namespace PrismMvvmDemo.Runner.ViewModels
    {
        public class MainWindowViewModel : BindableBase
        {
            private IApplicationCommands _applicationCommands;
            public IApplicationCommands ApplicationCommands
            {
                get { return _applicationCommands; }
                set
                {
                    SetProperty(ref _applicationCommands, value);
                }
            }
            public MainWindowViewModel(IApplicationCommands applicationCommands)
            {
                _applicationCommands = applicationCommands;
            }
        }
    }
    
    

    Views下的MainWindow.xaml代码。

    <Window x:Class="PrismMvvmDemo.Runner.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:prism="http://prismlibrary.com/"
            prism:ViewModelLocator.AutoWireViewModel="True"
            mc:Ignorable="d"  
            Title="MainWindow" Height="450" Width="800">
        <Window.Resources>
            <Style TargetType="TabItem">
                <Setter Property="Header" Value="{Binding DataContext.Title}"/>
            </Style>
        </Window.Resources>
      
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TabControl prism:RegionManager.RegionName="TabControlRegion"/>
            <StackPanel Grid.Row="1">
                <Button Content="AllSave" Command="{Binding ApplicationCommands.AllSave}"/>
                <Button Content="CurrentSave" Command="{Binding ApplicationCommands.CurrentSave}"/>
            </StackPanel>
        </Grid>
    </Window>
    
    

    最终运行的效果图

    我创建了一个C#相关的交流群。用于分享学习资料和讨论问题。欢迎有兴趣的小伙伴:QQ群:542633085

  • 相关阅读:
    《那些年啊,那些事——一个程序员的奋斗史》——29
    《那些年啊,那些事——一个程序员的奋斗史》——33
    《那些年啊,那些事——一个程序员的奋斗史》——34
    《那些年啊,那些事——一个程序员的奋斗史》——36
    《那些年啊,那些事——一个程序员的奋斗史》——31
    抽像类和接口的区别
    编码规范
    网站色彩搭配技术.
    读取(写入)配置文件
    异步调用和多线程调用
  • 原文地址:https://www.cnblogs.com/duwenlong/p/15120314.html
Copyright © 2011-2022 走看看