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

news/2024/9/21 16:26:19

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.ryyt.cn/news/57516.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

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…

2024软件工程第一次作业

这个作业属于哪个课程 软件工程这个作业要求在哪里 作业要求这个作业的目标 1. 使用AIGC工具 2. 搭建GitHub主页 3. 掌握Markdown编辑 4. 学会写博客学号 1022021451. 个人 Logo 设计 🎨 🎯 设计理念 我的个人风格倾向于 可爱 和 温馨,博客主题围绕 创意 和 艺术。因此,我…

【工具推荐】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文件上传…