WPF中如何实现页面的切换和Window的跳转切换_wpf 3级界面 mvvm 通过指令 返回上一级界面-程序员宅基地

技术标签: WPF  wpf  

总目录



前言

本文主要讲述如何在同一个窗体内,实现不同功能模块的页面切换。


一、准备工作

1.搭建一个简单的mvvm项目结构

在这里插入图片描述
首先搭建一个简单的项目框架,然后有红和绿两个页面,ViewModels中的Base 中简单实现了ICommand 和 INotifyPropertyChanged接口

2.实现ICommand 和 INotifyPropertyChanged接口

    public class CommandBase : ICommand
    {
    
        public event EventHandler CanExecuteChanged;

        public CommandBase(Action executeAction, Func<bool> canExcuteFunc = null)
        {
    
            this.ExecuteAction = executeAction;
            this.CanExecuteFunc = canExcuteFunc;
        }

        public CommandBase(Action<object> executeParaAction, Func<object, bool> canExecuteParaFunc = null)
        {
    
            this.ExecuteParaAction = executeParaAction;
            this.CanExecuteParaFunc = canExecuteParaFunc;
        }

        public bool CanExecute(object parameter=null)
        {
    
            if (parameter==null)
            {
    
                return this.CanExecuteFunc == null ? true : CanExecuteFunc.Invoke();
            }
            return CanExecuteParaFunc == null ? true : CanExecuteParaFunc.Invoke(parameter);
        }

        public void Execute(object parameter = null)
        {
    
            if (parameter == null)
            {
    
                ExecuteAction?.Invoke();
            }
            else
            {
    
                ExecuteParaAction?.Invoke(parameter);
            }
        }

        public Action ExecuteAction {
     get; set; }
        public Func<bool> CanExecuteFunc {
     get; set; }
        public Action<object> ExecuteParaAction {
     get; set; }
        public Func<object,bool> CanExecuteParaFunc {
     get; set; }
    }
    public class ViewModelBase : INotifyPropertyChanged
    {
    
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged([CallerMemberName]string name="")
        {
    
            PropertyChanged?.Invoke(this,new PropertyChangedEventArgs(name));
        }
    }

二、页面切换

1.使用Frame控件的方式实现

利用Frame的Source 属性加载内部的控件,使用Frame的时候,用于切换的页面可以是UserControl 或者Page,如案例中使用的就是Page

实现代码如下:

<Window x:Class="WpfApp2.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp2.Views"
        xmlns:vm="clr-namespace:WpfApp2.ViewModels"
        mc:Ignorable="d"
        Title="MainView" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainViewModel></vm:MainViewModel>
    </Window.DataContext>
    <DockPanel Grid.Column="0">
        <StackPanel Background="LightBlue">
            <RadioButton IsChecked="True" Command="{Binding ChangePageCommand}" CommandParameter="PageRedView.xaml" Content="红色" Margin="10"></RadioButton>
            <RadioButton Command="{Binding ChangePageCommand}" CommandParameter="PageGreenView.xaml" Content="绿色" Margin="10"></RadioButton>
        </StackPanel>
        <Frame NavigationUIVisibility="Hidden" Source="{Binding PageName}"/>
    </DockPanel>
</Window>


注意:这里的CommandParameter传入的是PageRedView.xaml文件

    public class MainViewModel:ViewModelBase
    {
    
        private string _pageName;

        public string PageName
        {
    
            get {
     return _pageName; }
            set {
     _pageName = value; OnPropertyChanged(); }
        }

        public ICommand ChangePageCommand {
     get; set; }

        public MainViewModel()
        {
    
            ChangePageCommand = new CommandBase(ChangePage);
            ChangePage("UserControlRed.xaml");//默认选中红色界面
        }

        private void ChangePage(object obj)
        {
    
            PageName = obj.ToString();
        }
    }

2.使用反射的方式实现

使用反射+ContentControl 的方式也可使用页面切换,不过该方式下ContentControl 的Content不可以承接Page,但是可以承接UserControl。(Page只有Frame 和Window可以承接,)
首先将红色和绿色两个界面修改为UserControl并命名为UserControlRed和UserControlGreen ,然后修改代码如下:

    <DockPanel Grid.Column="0">
        <StackPanel Background="LightBlue">
            <RadioButton IsChecked="True" Command="{Binding ChangePageCommand}" CommandParameter="UserControlRed" Content="红色" Margin="10"></RadioButton>
            <RadioButton Command="{Binding ChangePageCommand}" CommandParameter="UserControlGreen" Content="绿色" Margin="10"></RadioButton>
        </StackPanel>
        <ContentControl Content="{Binding MainContent}"/>
    </DockPanel>
  public class MainViewModel:ViewModelBase
    {
    
        private FrameworkElement mainContent;

        public FrameworkElement MainContent
        {
    
            get {
     return mainContent; }
            set {
     mainContent = value; OnPropertyChanged(); }
        }


        public ICommand ChangePageCommand {
     get; set; }

        public MainViewModel()
        {
    
            ChangePageCommand = new CommandBase(ChangePage);
            ChangePage("UserControlRed");//默认选中红色界面
        }

        private void ChangePage(object obj)
        {
    
        	//【 * 】这里需要拼接路径
            Type type = Type.GetType("WpfApp2.Views." + obj.ToString());
            MainContent = (FrameworkElement)System.Activator.CreateInstance(type);
        }
    }

3.实现效果

在这里插入图片描述

三、Window的跳转切换

1、原生的跳转切换

在这里插入图片描述
上面的代码运行后会发现:

  • 仅仅弹出子窗口的时候发现,以ShowDialog()打开的窗体,要等窗体关闭后才能操作其他窗体.而Show()则不受此限制.(见Button_Click)
  • 在尝试Show() 和ShowDialog() 来跳转窗口的时候,Show 可行,ShowDialog不可行,因为Close方法会将两个窗口均关闭
    在这里插入图片描述
  • Show 和Close 的配合可以做一个简单的Window跳转切换,实现登录窗口 跳转到 主窗口的小功能

2、修改应用程序关闭方式实现窗体切换

  • 1 在App.xaml中修改如下:去掉StartupUri 的设置,然后将ShutdownMode 设置为OnExplicitShutdown

OnExplicitShutdown 表示只有手动调用Shutdown方法的时候,应用程序才会关闭,否则应用程序不会关闭,多用于 用户登录窗口, 过度到引用的主界面中

<Application x:Class="WpfApp2.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WpfApp2"
             ShutdownMode="OnExplicitShutdown">
    <Application.Resources>
         
    </Application.Resources>
</Application>
  • 2 在App.xaml.cs文件中重写OnStartup方法
        protected override void OnStartup(StartupEventArgs e)
        {
    
            base.OnStartup(e);
            new MainView().ShowDialog();
            new TestView().ShowDialog();
            App.Current.Shutdown();
        }

上面代码的意思,就是启动的时候 先启动并展示MainView这个窗口,然后当MainView的窗口关闭的时候,就展示TestView这个窗口,当TestView窗口关闭的时候,整个应用程序就会关闭。

这里就涉及到一个知识点:ShowDialog()和Show()的区别

  • ShowDialog就是模式窗体,使用ShowDialog()后,代码会”卡“在这里,后面的代码不会执行;直到使用ShowDialog显示的窗体被关闭,后面的代码才会执行。
    • 关闭窗体方式1:直接点击使用ShowDialog显示的窗体关闭按钮,关闭当前窗体,则可继续
    • 关闭窗体方式2:只要设置了窗体的DialogResult属性(true或false),窗体就会关闭退出
  • Show就是非模式的窗体,显示窗体后不论窗体是否关闭都执行Show后面的语句。
  • ShowDialog() 的返回值DialogResult 为 bool?类型,如果通过关闭按钮,关闭窗体,默认返回值为false
  • Show() 的返回值为void ,只管执行即可
  • 3 优化在App.xaml.cs文件中重写的OnStartup方法
    从扩展的【ShowDialog()和Show()的区别】知识点中我们知道,当使用关闭按钮,关闭窗体,默认返回值DialogResult为false,而通过设置DialogResult的值,无论true/false ,都可关闭窗体,因此我们要区分这两个场景:
    就比如当我们点击登录窗体的关闭窗口的时候,我们需要真正关闭窗口和应用程序;但是当我们点击登录成功的时候,我们需要的是关闭登录窗口,显示主窗口,因此可优化如下:
        protected override void OnStartup(StartupEventArgs e)
        {
    
            base.OnStartup(e);
            //增加一层判断,表示如果是点击窗体关闭按钮,则直接退出应用程序
            //另外这也要求我们在手动设置DialogResult的时候,需要设置为true,才可以将这个场景予以区分
            if (new MainView().ShowDialog()==true)
            {
    
                new TestView().ShowDialog();
            }
            App.Current.Shutdown();
        }
  • 4 在需要跳转的功能处设置DialogResult为true
    在这里插入图片描述

3、MVVM中Window的切换(初级)

还是借用【2、修改应用程序关闭方式实现窗体切换】+MVVM的方式,适用于登录界面过度到主窗口
在这里插入图片描述

4、MVVM中Window的交互

在这以上1-3小节介绍的都是,关闭一个窗口后,然后又需要展示一个窗口,多用于登录窗口到主窗口的过渡场景。但是在平常的开发不仅仅只有这一种场景,还有一种常见场景是在主窗口 点击某个信息列表的按钮,然后打开详情信息的窗口(主窗口不可关闭),此场景如果使用原生代码直接new xxxWindow().ShowDialog();即可。但是如果在MVVM解耦思想的项目下,在ViewModel中直接使用这样的代码,就达不到解耦的目的了。因此我们需要一个中间层来管理窗口和处理窗体间信息的传递。
由此我们可以定义一个WindowManager类,

    public class WindowManager
    {
    
        private static Dictionary<string, Action<object>> _dir = new Dictionary<string, Action<object>>();
        public static void Register(string key,Action<object> action)
        {
    
            if (!string.IsNullOrEmpty(key)&&!_dir.ContainsKey(key))
            {
    
                _dir.Add(key,action);
            }
        }

        public static void DoAction(string key,object obj)
        {
    
            if (!string.IsNullOrEmpty(key) && _dir.ContainsKey(key))
            {
    
                _dir[key]?.Invoke(obj);
            }
        }
    }

WindowManager 可以根据项目需求,做不同的扩展和兼容(如我们定义有返回值的委托Func,或者用泛型让该类更加通用),由上面的代码可知:该类的主要作用就是做了一个 处理窗体间传递信息用的集合,由于WindowManager第三方的身份,我们可以在任意地方使用Register 注册一个委托,然后使用DoAction执行委托,这样足以满足我们降低耦合的目的。

另外我们使用MVVM+WindowManager 写了一个增删改查的小案例,先看下实现的效果:
在这里插入图片描述
主要代码如下:
在这里插入图片描述
在这里插入图片描述
具体案例Demo(VS2019)的下载链接:关于MVVM下窗口间信息的传递案例demo


总结

以上就是今天要介绍的内容,希望以上内容可以帮助到你,如文中有不对之处,还请批评指正。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39847278/article/details/128386432

智能推荐

Colossal Fibonacci Numbers! UVA-11582_colossal fibonacci numbers! uva - 11582-程序员宅基地

文章浏览阅读118次。题意紫书第十章的题目。多组数据,每次给出三个数a,b,n,(a,b<2642^{64}264,n<=1000),问第aba^{b}ab个斐波那契数模n的值。斐波那契数列从f(0)=0,f(1)=1开始。题解因为是要求出f(ab)f(a^{b})f(ab)%n的值,所以先在%n条件下算出斐波那契数列。算到哪里结束呢?注意到在%n条件下,斐波那契数列如果前两个数相同,则由递推公式f(i)=f(i−1)+f(i−2)f(i)=f(i-1)+f(i-2)f(i)=f(i−1)+f(i−2),第三个_colossal fibonacci numbers! uva - 11582

HTML基础语法总结_html基本语法共有几种-程序员宅基地

文章浏览阅读454次。Leftmargin topmargin用来调整链接或文字位置Text用来调整字体颜色点击进入百度页面 添加链接alink点击时链接 link当前显示链接 vlink点击后链接 主要用来控制链接字体的颜色bgcolor用来设置背景颜色background用来设置背景图片(GIF、JPG)background=fixed使背景图片成固定效果,不随滚动条_html基本语法共有几种

分享10个超级实用的Python自动化脚本_python写一个脚本-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏262次。在日常的工作学习当中,我们总会遇到各式各样的问题,其中不少的问题都是一遍又一遍简单重复的操作,不妨直接用Python脚本来自动化处理。今天我就给大家分享10个Python高级脚本,帮助我们减少无谓的时间浪费,提高工作学习中的效率。 喜欢记得收藏、关注、点赞。注:完整版代码、数据、技术交流文末获取给照片添加水印给照片添加水印的代码多种多样,下面这种的或许是最为简单的形式,from PIL import Imagefrom PIL import ImageFontfrom PIL import _python写一个脚本

《信息学奥赛一本通 初赛真题解析》第四章 阅读程序 第9节 树和图_信息学奥赛一本通+"第四章 阅读程序"+filetype:pptx-程序员宅基地

文章浏览阅读60次。《信息学奥赛一本通 初赛真题解析》第四章 阅读程序 第9节 树和图_信息学奥赛一本通+"第四章 阅读程序"+filetype:pptx

Android 类似360悬浮窗口实现源码-程序员宅基地

文章浏览阅读195次。当我们在手机上安装360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫士的操作界面,而且该浮动窗口不受其他activity的覆盖影响仍然可见(多米音乐也有相关的和主界面交互的悬浮小窗口)。它能悬浮在手机桌面,且不受Activity界面的影响,说明该悬浮窗口是不隶属于Activity界面的,也就是说,他是隶属于启动它的应用程序所在进程。如360App所在的应用进程..._android 模仿360悬浮窗口

pgsql数据类型:二进制字符串_pgsql 如何把二进制文件直接放入数据库-程序员宅基地

文章浏览阅读2.7k次。二进制字符串名字存储尺寸描述bytea1或4字节外加真正的二进制串变长二进制串db=# create table demo_bytea(bytea bytea);插入插入文本db=# insert into demo_bytea values('123');INSERT 0 1db=# select * from demo_bytea; bytea---------- \x313233(1 row)插入转义类型这个不是很理解,什么叫转义类型,我的_pgsql 如何把二进制文件直接放入数据库

随便推点

(算法)通俗易懂的字符串匹配KMP算法及求next值算法_kmp算法next计算方法-程序员宅基地

文章浏览阅读10w+次,点赞979次,收藏2.6k次。大多数据结构课本中,串涉及的内容即串的模式匹配,需要掌握的是朴素算法、KMP算法及next值的求法。在考研备考中,参考严奶奶的教材,我也是在关于求next值的算法中卡了一下午时间,感觉挺有意思的,把一些思考的结果整理出来,与大家一起探讨。以下的顺序为1、最基本的朴素算法2、优化的KMP算法3、应算法需要定义的next值4、手动写出较短串的next值的方法5、最难理解的、足足有5行的代码..._kmp算法next计算方法

为什么工厂招工越来越难-程序员宅基地

文章浏览阅读688次。什么原因导致工厂招工越来越难?辞职农民工说出里面的心酸说到工厂,里面打工的人,几乎都是农村的,每年一过完年出来打工,他们都是直奔工厂,因为条件有限,他们在大城市根本没有更好的选择,只有选择工厂上班,毕竟工厂对学历没有太大的要求,要知道,现在大学生找工作都难,何况这些从农村出来的,可是现在,工厂为什么越来越难招工?这是什么原因导致的,很多人辞职去外面做事,也..._招工难博客

Vue.js菜鸟教程自学(一)_vue.js 菜鸟教程-程序员宅基地

文章浏览阅读1.1w次,点赞2次,收藏19次。导入js:一、hello vue<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Document</title><script src="https://cdn.staticfile.org/vue/2.2.2/vue...._vue.js 菜鸟教程

提高社群活跃度的10个方法_房产社群提升群活跃度-程序员宅基地

文章浏览阅读243次。要想做好社群运营,除了朋友圈搭建、线下活动组织、产品制度设计外,最重要的一点就是群内的维护。群内的维护也不仅仅是靠群规、群主的维系,还要配合一些活动来提升社群的活跃、用户参与和认同。今天为大家罗列一些,做社群运营常用的一些促活、催单的小互动、小游戏。因为不同社群适合的活动不一样的,因此大家根据自己的业务类型来做结合调整。1、群成员的介绍也可以叫群成员破冰,这种比较适合资源型的社群、或者有门槛的小群,给每个成员提供介绍的模板,入群的时候发到群里面。既能避免入群的尴尬冷场,又能方便大家今后资源上的互_房产社群提升群活跃度

处理JAVAsplit分割字符串遇到字段中包含转义了的分隔符_java转义了特殊字符但是需要分号分隔-程序员宅基地

文章浏览阅读229次。在Java中,使用"|“作为分隔符时,可以使用转义字符”|"来表示真正的分隔符。但是,如果字段中包含了"aaa|bb"这样的内容,Java会将其视为分隔符,导致分割结果不正确。为了解决这个问题,可以使用正则表达式的预先查找(lookahead)和后顾查找(lookbehind)来匹配分隔符,而不是直接使用分隔符本身。在上述代码中,使用了正则表达式"(?\)|“来匹配分隔符”|“。\)“表示前面不能有转义字符”",这样就可以排除掉被转义的分隔符。这样就能够正确地将字段中包含转义字符的内容保留在分割结果中。_java转义了特殊字符但是需要分号分隔

NGINX重定向保留域名等信息_ng 保持当前域名-程序员宅基地

文章浏览阅读2.4k次。nginx_ng 保持当前域名

推荐文章

热门文章

相关标签