WPF在UserControl使用MVVM模式实现窗体移动,最大化,最小化,关闭

news/2024/9/19 23:44:25/文章来源:https://www.cnblogs.com/guchen33/p/18401269

1、在WPF中,我们移动窗体,可以使用MouseDown或者MouseLeftButtonDown去触发DragMove方法
2、当我们使用UserControl的时候,它是没有DragMove方法的,这个时候怎么办
我们改为命令的形式,可以直接调出当前的窗体,或者将窗体当参数传入到ViewModel,也没问题
我写了

<i:Interaction.Triggers><i:EventTrigger EventName="MouseDown" SourceName="GridButton"><i:InvokeCommandAction Command="{Binding DragmoveCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers>

当然,它内部是可以传递参数的,我们能成功实现,
但是,注意了,注意了,
如果我们的xaml具有多个事件命令,此时就会出现问题

 <Grid Background="#FF0078D7" x:Name="GridButton" ><i:Interaction.Triggers><i:EventTrigger EventName="MouseDown" SourceName="GridButton"><i:InvokeCommandAction Command="{Binding DragmoveCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers><TextBlockMargin="10,0,0,0"VerticalAlignment="Center"Foreground="White"Text="电子雷管" /><StackPanelHorizontalAlignment="Right"VerticalAlignment="Center"Orientation="Horizontal"><TextBlock Foreground="White" Text="—" x:Name="MinimizeButton"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown" SourceName="MinimizeButton"><i:InvokeCommandAction Command="{Binding MinCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlockMargin="15,0,15,0"Foreground="White"Text="☐"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown" ><i:InvokeCommandAction Command="{Binding MaxCommand, Mode=OneWay}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlockMargin="0,0,15,0"Foreground="White"Text="✕"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding CloseCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock></StackPanel></Grid>

界面如下
image
上面就是我的整体全部代码,现在我移动,最大化,关闭功能都是好的,但是增加了移动的功能后,点击最小化,移动的方法出问题,说CurrentWindow是null,我打断点查看了一下,增加移动的功能后,我点击最大化,先执行ExecuteDragmove在执行最大化,在执行ExecuteDragmove,我猜想是WPF的<i:Interaction.Triggers>有路由的功能,虽然最大化的前后都执行一次ExecuteDragmove方法,但是我的界面是使用 return Application.Current.Windows.OfType().SingleOrDefault(w => w.IsActive);获取的,只有在电脑屏幕活动,哪怕在最大化前后各执行一次ExecuteDragmove方法也不会出错,但是最小化功能不同,触发最小化的时候,它肯定前后也会走路由,也就是点击最小化前后都会执行一次ExecuteDragmove方法,但似乎最小化的同时,屏幕上没有活动的窗体,所以ExecuteDragmove方法中CurrentWindow将会是null,所以解决的方法应该关闭路由
因此我使用了SourceName,但是它只能阻止最大化之前和最小化之前不会触发移动,
首先,我想到了附加属性

 public static class WindowHelper{public static readonly DependencyProperty DragMoveProperty =DependencyProperty.RegisterAttached("DragMove", typeof(bool), typeof(WindowHelper), new PropertyMetadata(false, OnDragMoveChanged));public static bool GetDragMove(DependencyObject obj){return (bool)obj.GetValue(DragMoveProperty);}public static void SetDragMove(DependencyObject obj, bool value){obj.SetValue(DragMoveProperty, value);}private static void OnDragMoveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e){if (d is UIElement element && (bool)e.NewValue){element.MouseDown += Element_MouseDown;}}private static void Element_MouseDown(object sender, MouseButtonEventArgs e){if (e.LeftButton == MouseButtonState.Pressed){var window = ((FrameworkElement)sender).TemplatedParent as Window;if (window != null){window.DragMove();}}}}

然后Grid使用local:WindowHelper.DragMove="True"
但是我使用的是UserControl所以他不起作用
因此我想当了行为,我将Window的DragMove方法封装成行为给Grid,

在WPF中,行为(Behavior)是一种设计模式,它允许您将可重用的功能附加到UI元素上,而无需修改元素本身的代码。行为通常用于封装复杂的交互逻辑,使其更易于管理和复用。
在WPF中,事件路由有两种主要类型:直接路由(Direct Routing)和冒泡路由(Bubble Routing)。此外,还有一种隧道路由(Tunneling Routing),但它在WPF中不如直接和冒泡路由常见。

直接路由(Direct Routing)
直接路由事件不会在元素树中传播。它们仅在触发事件的元素上触发,并且仅触发一次。Behavior通常使用直接路由,因为它们是直接附加到特定元素上的。

冒泡路由(Bubble Routing)
冒泡路由事件从触发事件的元素开始,然后向上遍历元素树,直到到达根元素。这意味着如果一个子元素触发了事件,该事件会首先在子元素上触发,然后在其父元素上触发,依此类推,直到到达根元素。

隧道路由(Tunneling Routing)
隧道路由事件从根元素开始,向下遍历元素树,直到到达触发事件的元素。这种类型的路由在WPF中主要用于处理预览事件(例如PreviewMouseDown),它们在相应的冒泡事件之前发生。

事件路由顺序
在WPF中,事件的路由顺序是:

隧道事件(从根到目标)
直接事件(在目标上)
冒泡事件(从目标到根)
您的问题分析
在我的代码中,使用了EventTrigger来处理鼠标左键按下事件。默认情况下,EventTrigger使用冒泡路由。这就是为什么当我点击最小化按钮时,首先触发了Grid上的拖动行为,然后才触发了最小化按钮上的最小化行为。

<i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding DragmoveCommand, UpdateSourceTrigger=PropertyChanged}" />
</i:EventTrigger>

由于冒泡路由的特性,当我点击最小化按钮时,事件首先在按钮上触发,然后向上冒泡到包含它的Grid,最后到达根元素。这就是为什么会看到先执行拖动行为,然后执行最小化行为的原因。
最后写上完整的成功后的代码

  <Grid Background="#FF0078D7" x:Name="GridButton" ><i:Interaction.Behaviors><local:DragBehavior /></i:Interaction.Behaviors><TextBlockMargin="10,0,0,0"VerticalAlignment="Center"Foreground="White"Text="电子雷管" /><StackPanelHorizontalAlignment="Right"VerticalAlignment="Center"Orientation="Horizontal"><TextBlock Foreground="White" Text="—" x:Name="MinimizeButton"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown" SourceName="MinimizeButton"><i:InvokeCommandAction Command="{Binding MinCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlockMargin="15,0,15,0"Foreground="White"Text="☐"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown" ><i:InvokeCommandAction Command="{Binding MaxCommand, Mode=OneWay}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock><TextBlockMargin="0,0,15,0"Foreground="White"Text="✕"><i:Interaction.Triggers><i:EventTrigger EventName="MouseLeftButtonDown"><i:InvokeCommandAction Command="{Binding CloseCommand, UpdateSourceTrigger=PropertyChanged}" /></i:EventTrigger></i:Interaction.Triggers></TextBlock></StackPanel></Grid>
 public class HeaderViewModel:ControlViewModelBase{private Window CurrentWindow=>GetCurrentWindow();public HeaderViewModel(){DragmoveCommand = MinidaoCommand.Create(ExecuteDragmove);MinCommand = MinidaoCommand.Create(ExecuteMin);MaxCommand = MinidaoCommand.Create(ExecuteMax);CloseCommand = MinidaoCommand.Create(ExecuteClose);}#region--命令--public ICommand DragmoveCommand {  get; set; }public ICommand MinCommand { get; set; }public ICommand MaxCommand { get; set; }public ICommand CloseCommand { get; set;}#endregion#region--方法--private Window GetCurrentWindow(){return Application.Current.Windows.OfType<Window>().SingleOrDefault(w => w.IsActive);}private void ExecuteDragmove(){CurrentWindow.DragMove();}private void ExecuteClose(){Application.Current.Shutdown();}private void ExecuteMax(){WindowState state = CurrentWindow.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;CurrentWindow.WindowState = state;}private void ExecuteMin(){if (CurrentWindow.WindowState == WindowState.Maximized || CurrentWindow.WindowState == WindowState.Normal){CurrentWindow.WindowState = WindowState.Minimized;}}#endregion}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/793390.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ATTCK红队评估(红日靶场2)CS篇

靶机介绍红队实战系列,主要以真实企业环境为实例搭建一系列靶场,通过练习、视频教程、博客三位一体学习。本次红队环境主要Access Token利用、WMI利用、域漏洞利用SMB relay,EWS relay,PTT(PTC),MS14-068,GPP,SPN利用、黄金票据/白银票据/Sid History/MOF等攻防技术。关…

esp32笔记[18]-使用汇编在riscv架构的esp32c3点灯

使用esp-idf工具链编译汇编程序实现在riscv架构的esp32c3点灯. Compiling an assembly program using the esp-idf toolchain to blink an LED on the RISC-V based ESP32-C3.摘要 使用esp-idf工具链编译汇编程序实现在riscv架构的esp32c3点灯. Abstract Compiling an assembly…

一、编程语言简介与C++

编程语言是编程的工具 计算机系统是分层的编程语言是软件,也i是分层的

AI证件照,抠图、换背景、任意尺寸...有了这个神器,证件照通通自己搞定(本地化部署教程)

最近有个Github开源的AI证件照神器火了,以后再也不用专门跑一趟照相馆拍证件照了! 你随手一张日常生活照或自拍,上传到它那里,分分钟就能帮你换上。 蓝底、白底,抠掉杂乱的背景,生成各种尺寸规格的证件照。 这款名叫HivisionIDPhotos的开源工具,它不仅能帮你便捷地制作出免冠白…

软件工程导论作业 2:python实现论文查重

github项目地址这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13229这个作业的目标 通过Python开发个人项目,实现项目单元测试1.PSP表格PSP2.1 Personal Software P…

【工具推荐】Jeecg_Tools v1.0(最新版) - jeecg框架一键漏洞利用getshell

工具介绍: Jeecg_Tools是一款jeecg框架漏洞利用工具。本工具为jeecg框架漏洞利用工具非jeecg-boot! 下载链接: 链接:https://pan.quark.cn/s/9a1016a03402使用说明 运行于jdk8环境 java -jar Jeecg_Tools-1.0-java8.jar功能: 包含poc: 登录绕过检测 jeecgFormDemo文件上传…

必应每日壁纸API封装

简介 这个类封装了必应首页的每日壁纸查看功能,提供了查看、保存壁纸的方法,最大支持查看近8天的壁纸 使用方法 async Task Main() {try{var bing = BingWallpaperAPI.CreateInstance(8); //初始化,参数8表示一共会加载8张图片var task = await bing.Current();task.Wallpap…

第17篇 RabbitMQ安装详细步骤

一.RabbitMQ是什么? RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。 ​ AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。…

How to create the Gold gold using RGB color values All In One

How to create the Gold gold using RGB color values All In OneHow to create the Gold gold using RGB color values All In One如何使用 RGB 颜色值创建金色Gold (Golden) Color Color Name: Gold (Golden) Hex Color Code: #FFD700 RGB Color Code: RGB(255, 215, 0) CMYK…

代码整洁之道--读书笔记(3)

代码整洁之道简介: 本书是编程大师“Bob 大叔”40余年编程生涯的心得体会的总结,讲解要成为真正专业的程序员需要具备什么样的态度,需要遵循什么样的原则,需要采取什么样的行动。作者以自己以及身边的同事走过的弯路、犯过的错误为例,意在为后来者引路,助其职业生涯迈上更…

谈谈Spring中的BeanPostProcessor接口(转)

原文:谈谈Spring中的BeanPostProcessor接口 作者:特务依昂一. 前言这几天正在复习Spring的相关内容,在了解bean的生命周期的时候,发现其中涉及到一个特殊的接口——BeanPostProcessor接口。由于网上没有找到比较好的博客,所有最后花了好几个小时,通过Spring的官方文档对它…