如何在WPF MVVM模式下用C#通过Interaction Triggers实现窗口最大化最小化?
解决WPF中用Interaction Triggers实现窗口最大化/最小化(.NET 4.5,不破坏MVVM)
问题原因
你原来的方法无效,是因为WPF的Window类根本没有Maximize或Minimize这两个公共方法——它是通过WindowState属性来控制窗口状态的(可选值:WindowState.Normal/Maximized/Minimized),而CallMethodAction只能调用目标对象的公共方法,自然找不到对应方法导致失效。
下面给两种符合MVVM模式的解决方案:
方案1:自定义Interaction Action(View层处理,不侵入ViewModel)
这种方式适合把窗口状态控制逻辑留在View层,ViewModel不需要感知窗口细节。
步骤1:创建自定义TriggerAction
新建一个类,继承System.Windows.Interactivity.TriggerAction<DependencyObject>:
using System.Windows; using System.Windows.Interactivity; public class SetWindowStateAction : TriggerAction<DependencyObject> { public static readonly DependencyProperty TargetWindowProperty = DependencyProperty.Register("TargetWindow", typeof(Window), typeof(SetWindowStateAction)); public static readonly DependencyProperty TargetStateProperty = DependencyProperty.Register("TargetState", typeof(WindowState), typeof(SetWindowStateAction)); public Window TargetWindow { get => (Window)GetValue(TargetWindowProperty); set => SetValue(TargetWindowProperty, value); } public WindowState TargetState { get => (WindowState)GetValue(TargetStateProperty); set => SetValue(TargetStateProperty, value); } protected override void Invoke(object parameter) { if (TargetWindow != null) { TargetWindow.WindowState = TargetState; } } }
步骤2:在XAML中使用
替换原来的CallMethodAction,用这个自定义Action:
<!-- 先引入命名空间,假设你的自定义类在local命名空间下 --> xmlns:local="clr-namespace:你的项目命名空间" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" <!-- 最大化按钮 --> <Button Content="□" Height="20" Width="20"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <local:SetWindowStateAction TargetWindow="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" TargetState="Maximized"/> </i:EventTrigger> </i:Interaction.Triggers> </Button> <!-- 最小化按钮 --> <Button Content="−" Height="20" Width="20"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <local:SetWindowStateAction TargetWindow="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" TargetState="Minimized"/> </i:EventTrigger> </i:Interaction.Triggers> </Button> <!-- 还原按钮(如果需要) --> <Button Content="▭" Height="20" Width="20"> <i:Interaction.Triggers> <i:EventTrigger EventName="Click"> <local:SetWindowStateAction TargetWindow="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" TargetState="Normal"/> </i:EventTrigger> </i:Interaction.Triggers> </Button>
方案2:MVVM完全解耦(ViewModel控制,通过服务/附加属性)
如果希望ViewModel能控制窗口状态(比如根据业务逻辑自动最大化),可以用依赖注入或附加属性实现完全解耦。
方式A:窗口服务接口解耦
- 定义服务接口:ViewModel只依赖接口,不直接引用Window
public interface IWindowService { void MinimizeWindow(); void MaximizeWindow(); void RestoreWindow(); void CloseWindow(); }
- 实现服务(View层):在窗口类中实现这个接口
public partial class MainWindow : Window, IWindowService { public MainWindow() { InitializeComponent(); // 把服务注入ViewModel,比如通过构造函数 DataContext = new MainViewModel(this); } public void MinimizeWindow() => WindowState = WindowState.Minimized; public void MaximizeWindow() => WindowState = WindowState.Maximized; public void RestoreWindow() => WindowState = WindowState.Normal; public void CloseWindow() => Close(); }
- ViewModel中使用服务:
public class MainViewModel { private readonly IWindowService _windowService; public ICommand MinimizeCommand { get; } public ICommand MaximizeCommand { get; } public MainViewModel(IWindowService windowService) { _windowService = windowService; MinimizeCommand = new RelayCommand(_windowService.MinimizeWindow); MaximizeCommand = new RelayCommand(_windowService.MaximizeWindow); } }
(注:RelayCommand是WPF中常见的命令实现类,如果没有自己的,可以写一个简单版本,或者用Prism等框架的命令)
- XAML绑定命令:
<Button Content="−" Height="20" Width="20" Command="{Binding MinimizeCommand}"/> <Button Content="□" Height="20" Width="20" Command="{Binding MaximizeCommand}"/>
方式B:附加属性绑定(更轻量)
如果不想用服务,可以用附加属性把Window的WindowState绑定到ViewModel的属性:
- 创建附加属性类:
public static class WindowStateBehavior { public static readonly DependencyProperty BoundWindowStateProperty = DependencyProperty.RegisterAttached( "BoundWindowState", typeof(WindowState), typeof(WindowStateBehavior), new PropertyMetadata(WindowState.Normal, OnBoundWindowStateChanged)); public static WindowState GetBoundWindowState(DependencyObject obj) { return (WindowState)obj.GetValue(BoundWindowStateProperty); } public static void SetBoundWindowState(DependencyObject obj, WindowState value) { obj.SetValue(BoundWindowStateProperty, value); } private static void OnBoundWindowStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d is Window window) { window.WindowState = (WindowState)e.NewValue; } } }
- ViewModel中添加属性:
private WindowState _currentWindowState; public WindowState CurrentWindowState { get => _currentWindowState; set => SetProperty(ref _currentWindowState, value); // 假设实现了INotifyPropertyChanged } // 命令示例 public ICommand MinimizeCommand => new RelayCommand(() => CurrentWindowState = WindowState.Minimized); public ICommand MaximizeCommand => new RelayCommand(() => CurrentWindowState = WindowState.Maximized);
- XAML绑定附加属性:
<Window ... local:WindowStateBehavior.BoundWindowState="{Binding CurrentWindowState, Mode=TwoWay}"> <Button Content="−" Command="{Binding MinimizeCommand}"/> <Button Content="□" Command="{Binding MaximizeCommand}"/> </Window>
内容的提问来源于stack exchange,提问作者Tamal Banerjee
相关产品推荐
相关产品推荐

