WPF 实现点击按钮跳转页面功能

方法1.

配置环境

首先添加prism依赖项,配置好所有文件。需要配置的有两个文件:App.xaml.cs和App.xaml

App.xaml.cs

using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace PrismDemo
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
              
        }
    }
}

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 />
</prism:PrismApplication>

在MainView.xaml中添加下面两句话,引用prism并且实现自动关联上下文,即自动加载与视图关联的视图模型

xmlns:prism="http://prismlibrary.com/"

prism:ViewModelLocator.AutoWireViewModel="True"

创建两个文件:Views和ViewModels

**Views 文件夹:**包含所有的 XAML 文件,用于定义应用程序的用户界面。

每个 XAML 文件都对应一个界面,如 MainWindow.xaml、SettingsView.xaml 等等。

在 Views 文件夹中通常不会包含任何代码逻辑,只包含 XAML 标记。

**ViewModels 文件夹:**包含所有的 ViewModel 类,用于处理视图和模型之间的交互和状态管理。

每个 ViewModel 类都对应一个视图,如 MainWindowViewModel.cs、SettingsViewModel.cs 等等。

在 ViewModels 文件夹中通常不会包含任何与界面相关的代码,只包含处理视图和模型之间交互的业务逻辑。

在 MVVM 模式中,View 和 ViewModel 之间的通信通常使用数据绑定来实现。通过将 ViewModel 的属性和命令与 View 的元素绑定,可以将 ViewModel 中的数据和行为更新到 View 中。例如,可以将 ViewModel 中的一个属性绑定到 TextBlock 的 Text 属性,将一个命令绑定到 Button 的 Command 属性。这种数据绑定机制使得 View 和 ViewModel 之间的交互变得非常方便和灵活。

MainView.xaml

此段代码是页面展示部分,它定义了两行。第一行包含三个按钮,第二行包含一个内容控件。

每个按钮绑定了一个OpenCommand命令,并传递了一个字符串参数,用于打开不同的窗口。

内容控件 ContentControl 的内容被绑定到了一个 Body 属性,用于显示窗口中的主要内容。

<Window
    x:Class="PrismDemo.Views.MainView"
    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:local="clr-namespace:PrismDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    Title="MainWindow"
    Width="800"
    Height="450"
    prism:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewA"
                Content="打开模块A" />
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewB"
                Content="打开模块B" />
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewC"
                Content="打开模块C" />
        </StackPanel>
        <ContentControl Grid.Row="1" Content="{Binding Body}" />
    </Grid>
</Window>

ViewX.xaml

Views文件夹中一共有四个.xaml文件,除了主窗口的MainView.xaml外,其他三个分别是ViewA.xaml、ViewB.xaml 和 ViewC.xaml,分别对应于点击主窗口中按钮后的转换窗口,其中只有一行文本内容。

<TextBlock FontSize="80" Text="ViewX" /> (X=A, B, C)

MainViewModel.cs

MainViewModel.cs 继承自 Prism 库中的 BindableBase 类,实现了窗口布局中用到的命令和属性。

DelegateCommand<string> OpenCommand 是一个命令属性,用于绑定窗口中的三个按钮。

这个命令属性在构造函数中被初始化为一个 DelegateCommand<string> 对象,并指定了一个参数类型为字符串的委托方法 Open。这个委托方法用于执行不同的模块打开操作。

Body 属性是一个对象属性,用于绑定窗口布局中的内容控件。它的 get 方法返回 body 字段的值,而 set 方法则将传入的值设置给 body 字段,并通过 RaisePropertyChanged() 方法通知视图更新。

Open(string obj) 方法是 OpenCommand 委托方法的实现,它接受一个字符串参数 obj ,用于判断要打开哪个模块。根据参数值的不同,通过 Body 属性设置不同的视图。例如,当参数值为 "ViewA" 时,设置 Body 为一个 ViewA 的实例,从而实现了打开界面 A 的功能。

using Prism.Commands;
using Prism.Mvvm;
using PrismDemo.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismDemo.ViewModels
{
    internal class MainViewModel:BindableBase
    {
        //负责执行不同的模块
        public DelegateCommand<string> OpenCommand { get; private set; }
        public MainViewModel()
        {
            OpenCommand = new DelegateCommand<string>(Open);
        }
        private object body;
        public object Body
        {
            get { return body; }
            set { body = value; RaisePropertyChanged(); }
        }
        private void Open(string obj)
        {
            switch (obj)
            {           
                case "ViewA": Body = new ViewA(); break;
                case "ViewB": Body = new ViewB(); break;
                case "ViewC": Body = new ViewC(); break;
            }
        }
    }
}

方法2. 使用依赖注入动态设置内容

MainView.xaml

修改内容:

<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />

prism:RegionManager.RegionName="ContentRegion" 属性是使用 Prism 库中的区域管理器(Region Manager)功能来定义了一个名为 "ContentRegion" 的区域。

这个区域用于管理将要显示在 <ContentControl> 控件中的内容。

在这个区域中可以动态地加载和卸载不同的视图,从而实现复杂的界面交互和布局。

在 Prism 应用程序中,我们通常使用区域管理器来动态加载和卸载不同的视图,以实现应用程序的模块化和松耦合。例如,在一个具有多个区域的应用程序中,每个区域可以单独管理自己的视图,使得应用程序的不同模块之间可以相互独立地开发、测试和维护。

下面是MainView.xaml的完整代码:

<Window
    x:Class="PrismDemo.Views.MainView"
    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:local="clr-namespace:PrismDemo"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:prism="http://prismlibrary.com/"
    Title="MainWindow"
    Width="800"
    Height="450"
    prism:ViewModelLocator.AutoWireViewModel="True"
    mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal">
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewA"
                Content="打开模块A" />
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewB"
                Content="打开模块B" />
            <Button
                Margin="5"
                Command="{Binding OpenCommand}"
                CommandParameter="ViewC"
                Content="打开模块C" />
        </StackPanel>
        <ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
    </Grid>
</Window>

MainViewModel.cs

OpenCommand 属性与上面意义相同,用于在用户点击打开按钮时执行 Open 方法。

Open 方法根据传入的参数 obj 来决定要打开哪个模块,并通过区域管理器(IRegionManager)来请求加载指定模块的视图。

在构造函数中接受了一个 IRegionManager 类型的参数,用于从依赖注入容器中获取区域管理器的实例。这样,我们就可以通过注入区域管理器来使用 Prism 库中提供的区域管理器功能,从而实现对区域中的视图进行动态加载和卸载等操作。

using Prism.Commands;
using Prism.Mvvm;
using Prism.Regions;
using PrismDemo.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PrismDemo.ViewModels
{
    internal class MainViewModel:BindableBase
    {
        //负责执行不同的模块
        public DelegateCommand<string> OpenCommand { get; private set; }
        private readonly IRegionManager regionManager;
        //IRegionManager: 主要负责管理定义的所有可用区域
        public MainViewModel(IRegionManager regionManager)
        {
            OpenCommand = new DelegateCommand<string>(Open);
            this.regionManager = regionManager;
        }
        private void Open(string obj)
        {
            //在这个区域通过依赖注入动态设置内容,
            regionManager.Regions["ContentRegion"].RequestNavigate(obj);
        }
    }
}

App.xaml.cs

修改内容:

protected override void RegisterTypes(IContainerRegistry containerRegistry)

{

containerRegistry.RegisterForNavigation<ViewA>();

containerRegistry.RegisterForNavigation<ViewB>();

containerRegistry.RegisterForNavigation<ViewC>();

}

在使用 Prism 进行开发时,通常需要在应用程序启动时注册应用程序使用的服务和视图模型等对象,这个过程就叫做注册。

在 Prism 中,使用 IContainerRegistry 接口来完成依赖注入的注册过程,

RegisterForNavigation 方法是用来注册导航对象(视图)的方法。

该方法通过传入导航目标对象类型作为泛型参数,告诉 Prism 在使用该导航时应该使用哪个视图对象。因此,调用该方法将允许我们在 Prism 中使用导航和在容器中注入视图。

具体来说,RegisterForNavigation<ViewA>() 会将类型 ViewA 注册为一个可导航的视图对象,当需要导航到 ViewA 时,Prism 会自动创建该视图对象并将其添加到对应的区域中,而无需手动实例化该对象。

通常,在应用程序启动时,我们会在 App.xaml.cs 中重写 ConfigureContainerRegisterTypes 方法,从而将应用程序中需要的服务和对象注册到容器中,以便在整个应用程序中使用。

using Prism.DryIoc;
using Prism.Ioc;
using PrismDemo.Views;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace PrismDemo
{
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<ViewA>();
            containerRegistry.RegisterForNavigation<ViewB>();
            containerRegistry.RegisterForNavigation<ViewC>();      
        }
    }
}

Prism的模块化

创建多个项目,将项目输出类型设为类库。创建页面并创建类对此界面进行注册。

创建内容如下:

其中的ViewA、ViewB和MainViewModel.cs中的内容和方法2中相同。

ModuleAProfile.cs

这段代码定义了一个名为 ModuleAProfile 的类,实现了 Prism 库中的 IModule 接口。

在 Prism 中,模块是一种组织和管理功能的机制,通常使用模块将应用程序分解为可重用和独立的部分,从而简化开发和维护。

IModule 接口要求实现两个方法:RegisterTypesOnInitialized

在这个例子中,ModuleAProfile 类只实现了 RegisterTypes 方法。在该方法中,通过调用 containerRegistry.RegisterForNavigation<ViewA>() 方法来将 ViewA 类型注册为可导航的视图。在这里,RegisterForNavigation 方法是 Prism 库提供的一种方便的方法,用于在应用程序中注册可导航的视图,使其能够通过 URI 进行导航。

using ModuleA.Views;
using Prism.Ioc;
using Prism.Modularity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ModuleA
{
    public class ModuleAProfile : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<ViewA>();
        }
    }
}

ModuleBProfile.cs

using ModuleB.Views;
using Prism.Ioc;
using Prism.Modularity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ModuleB
{
    public class ModuleBProfile : IModule
    {
        public void OnInitialized(IContainerProvider containerProvider)
        {
            
        }

        public void RegisterTypes(IContainerRegistry containerRegistry)
        {
            containerRegistry.RegisterForNavigation<ViewB>();
        }
    }
}

App.xaml.cs

创建完上面两个ModuleAProfile和ModuleB文件之后,要在主页面中加入依赖项 ,而此文件的主要目的是使用ConfigureModuleCatalog() 方法来配置应用程序的模块目录。

ConfigureModuleCatalog() 方法里添加了两个模块 ModuleAProfileModuleBProfile

因此,这个应用程序使用 DryIoc 作为 IoC 容器,创建了一个主窗口,并配置了两个模块。这些模块将在应用程序启动时被加载。

using ModuleA;
using ModuleB;
using Prism.DryIoc;
using Prism.Ioc;
using Prism.Modularity;
using PrismDemo.Views;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace PrismDemo
{
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        { 
        }
        protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
        {
            moduleCatalog.AddModule<ModuleAProfile>();
            moduleCatalog.AddModule<ModuleBProfile>();
            base.ConfigureModuleCatalog(moduleCatalog);
        }
    }
}

引用ModuleAProfile 和 ModuleBProfile的第二种方式

  1. 不添加依赖项
  2. 修改App.xaml.cs
  3. 在主程序文件的指定位置创建文件Modules,将ModuleA和ModuleB的dll文件移动到此文件中。

此方法比上面引用方法耦合性更低,不需要指定调用哪个模块,ModuleA、ModuleB和主程序直接无直接联系。

App.xaml.cs

using Prism.DryIoc;
using Prism.Ioc;
using Prism.Modularity;
using PrismDemo.Views;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace PrismDemo
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : PrismApplication
    {
        protected override Window CreateShell()
        {
            return Container.Resolve<MainView>();
        }
        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {


        }
        protected override IModuleCatalog CreateModuleCatalog()
        {
            return new DirectoryModuleCatalog() { ModulePath = @".\Modules" };

        }
    }
}

文件创建位置

相关推荐
Crazy Struggle15 小时前
.NET 8.0 通用管理平台,支持模块化、WinForms 和 WPF
vue·wpf·winform·.net 8.0·通用权限管理
呆萌很2 天前
HCIP-HarmonyOS Application Developer 习题(二十)
华为·wpf·harmonyos
九鼎科技-Leo2 天前
什么是 WPF 中的转换器?如何自定义一个值转换器?
开发语言·windows·c#·.net·wpf
为风而战2 天前
Deepin 系统中安装Rider和Uno Platform
linux·运维·.net·wpf
△曉風殘月〆2 天前
WPF MVVM入门系列教程(三、数据绑定)
c#·wpf·mvvm
^@^lemon tea^@^2 天前
.NET6中WPF项目添加System.Windows.Forms引用
wpf·.net6·windows.forms引用
九鼎科技-Leo2 天前
在 WPF 中,绑定机制是如何工作的?WPF数据绑定机制解析
windows·c#·.net·wpf
就是有点傻3 天前
WPF自定义翻页控件
wpf
eggcode3 天前
使用ookii-dialogs-wpf在WPF选择文件夹时能输入路径
c#·wpf
code bean3 天前
【wpf】ResourceDictionary 字典资源的用法
windows·c#·wpf