绑定“WindowState”使用 MVVM 的 WPF 中窗口的属性

发布于 2024-10-11 15:48:05 字数 149 浏览 8 评论 0原文

我将主窗口的“WindowState”属性绑定到 ViewModel,以便通过命令更改窗口的状态,但第一次最小化窗口时,它会像 Excel 文件中的工作表一样最小化。是否有解决此问题的方法或将“WindowState”属性绑定到我的 ViewModel 以便窗口正确最小化的正确方法?

I bound the "WindowState" property of my main window to my ViewModel in order to change the state of the window by a command, but the first time I minimize the window it minimizes like a worksheet does in an Excel file. Is there a work around for this or a correct way to bind the "WindowState" property to my ViewModel so that the window minimizes correctly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

芯好空 2024-10-18 15:48:05

这是一个使用中继命令逻辑进行测试的示例解决方案。您将获得有关采用模型-视图-视图模型设计模式的 WPF 应用程序的更多详细信息。

<Window x:Class="WpfMvvmTestCSharp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:WpfMvvmTestCSharp"
    Title="Window1" Height="300" Width="300" WindowState="{Binding CurWindowState, Mode=TwoWay}">
    <Window.DataContext>
        <vm:Window1ViewModel/>
    </Window.DataContext>
    <Grid>
        <Button Command="{Binding CmdMax}" Height="23" Margin="12,25,0,0" Name="button1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="75">Maximize</Button>
        <Button Command="{Binding CmdMin}" Height="23" Margin="101,25,102,0" Name="button2" VerticalAlignment="Top">Minimize</Button>
        <Button Command="{Binding CmdRes}" Height="23" HorizontalAlignment="Right" Margin="0,25,13,0" Name="button3" VerticalAlignment="Top" Width="75">Restore</Button>
    </Grid>
</Window>

并在 Windows ViewModel 中

class Window1ViewModel:ViewModelBase
    {
        public Window1ViewModel()
        {
            CurWindowState = WindowState.Maximized;
        }

        public ICommand CmdMax
        {
            get { return new RelayCommand(param => onCmdMax()); }
        }

        void onCmdMax()
        {
            CurWindowState = WindowState.Maximized;
        }
        public ICommand CmdMin
        {
            get { return new RelayCommand(param => onCmdMin()); }
        }
        void onCmdMin()
        {
            CurWindowState = WindowState.Minimized;
        }
        public ICommand CmdRes
        {
            get { return new RelayCommand(param => onCmdRes()); }
        }

        void onCmdRes()
        {
            CurWindowState = WindowState.Normal;
        }

        private WindowState _curWindowState;
        public WindowState CurWindowState
        {
            get
            {
                return _curWindowState;
            }
            set
            {
                _curWindowState = value;
                base.OnPropertyChanged("CurWindowState");
            }
        }
    }

this is a sample work around that tested with Relaying Command Logic. You will get more detail on WPF Apps With The Model-View-ViewModel Design Pattern .

<Window x:Class="WpfMvvmTestCSharp.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:WpfMvvmTestCSharp"
    Title="Window1" Height="300" Width="300" WindowState="{Binding CurWindowState, Mode=TwoWay}">
    <Window.DataContext>
        <vm:Window1ViewModel/>
    </Window.DataContext>
    <Grid>
        <Button Command="{Binding CmdMax}" Height="23" Margin="12,25,0,0" Name="button1" VerticalAlignment="Top" HorizontalAlignment="Left" Width="75">Maximize</Button>
        <Button Command="{Binding CmdMin}" Height="23" Margin="101,25,102,0" Name="button2" VerticalAlignment="Top">Minimize</Button>
        <Button Command="{Binding CmdRes}" Height="23" HorizontalAlignment="Right" Margin="0,25,13,0" Name="button3" VerticalAlignment="Top" Width="75">Restore</Button>
    </Grid>
</Window>

and in the Windows ViewModel

class Window1ViewModel:ViewModelBase
    {
        public Window1ViewModel()
        {
            CurWindowState = WindowState.Maximized;
        }

        public ICommand CmdMax
        {
            get { return new RelayCommand(param => onCmdMax()); }
        }

        void onCmdMax()
        {
            CurWindowState = WindowState.Maximized;
        }
        public ICommand CmdMin
        {
            get { return new RelayCommand(param => onCmdMin()); }
        }
        void onCmdMin()
        {
            CurWindowState = WindowState.Minimized;
        }
        public ICommand CmdRes
        {
            get { return new RelayCommand(param => onCmdRes()); }
        }

        void onCmdRes()
        {
            CurWindowState = WindowState.Normal;
        }

        private WindowState _curWindowState;
        public WindowState CurWindowState
        {
            get
            {
                return _curWindowState;
            }
            set
            {
                _curWindowState = value;
                base.OnPropertyChanged("CurWindowState");
            }
        }
    }
没︽人懂的悲伤 2024-10-18 15:48:05

我认为您不应该关心视图模型中的窗口状态,这是完全错误的,因为较低级别的层知道较高级别的层(因此错误的关注点分离(SOC))。

在这种情况下,我通常做的是从包含视图模型的控件或窗口(即视图)的代码隐藏中订阅视图模型中的更改。在这种情况下,在代码隐藏中编写代码是有效的,因为它仅在视图中使用(因此代码隐藏是此逻辑的完美位置,您确实不想对其进行单元测试)。

I don't think you should care about the window state in a view model, it's completely wrong because a lower-level layer is aware of a higher-level layer (thus wrong Separation of Concerns (SOC)).

What I normally do in this case is subscribe to changes in the view model from the code-behind of the control or window (thus the view) containing the view model. In this case, it is valid to write code in the code-behind because it is only used in the view (and thus the code-behind is the perfect location for this logic, which you really don't want to unit test).

北渚 2024-10-18 15:48:05

另一个需要考虑的选项是通过命令和事件来订阅后台代码,例如:

    <Button Command="{Binding SnoozeCommand}" Click="Button_Click">Snooze</Button>

本例中的命令会影响虚拟机。 Click 事件仅改变窗口状态。

Another option to consider is subscribing both via a command AND an event to code behind, e.g:

    <Button Command="{Binding SnoozeCommand}" Click="Button_Click">Snooze</Button>

The command in this case affects the VM. The Click event, only changes the Window state.

攒一口袋星星 2024-10-18 15:48:05

我已经找到了自己的解决方案,它非常适合 MVVM。我使用行为来查找用户控件的父窗口并跟踪 WindowState 更改。

public class WindowStateBehavior : Behavior<UserControl>
{
    public static readonly DependencyProperty WindowStateProperty =
        DependencyProperty.Register(nameof(WindowState), typeof(WindowState), typeof(WindowStateBehavior),
                                    new FrameworkPropertyMetadata(default(WindowState), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    private Window window;

    public WindowState WindowState
    {
        get => (WindowState) GetValue(WindowStateProperty);
        set => SetCurrentValue(WindowStateProperty, value);
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.window = Window.GetWindow(this.AssociatedObject)!;
        this.window.StateChanged += this.OnStateChanged;
    }

    private void OnStateChanged(object sender, EventArgs e) => this.WindowState = this.window.WindowState;
}

此行为可以在任何 UserControl 中使用,就像在 ViewModel 中绑定 WindowState 一样。

<UserControl x:Class="RCBase.WPF.Monitor.CustomUserView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:behaviors="clr-namespace:RCBase.WPF.Behaviors"
         mc:Ignorable="d" d:DataContext="{d:DesignInstance monitor:CustomUserViewModel}">
<i:Interaction.Behaviors>
    <behaviors:WindowStateBehavior WindowState="{Binding WindowState}" />
</i:Interaction.Behaviors>

I have found my own solution which is perfectly suited to MVVM. I'm using behavior to find the parent window of the user control and track WindowState changes.

public class WindowStateBehavior : Behavior<UserControl>
{
    public static readonly DependencyProperty WindowStateProperty =
        DependencyProperty.Register(nameof(WindowState), typeof(WindowState), typeof(WindowStateBehavior),
                                    new FrameworkPropertyMetadata(default(WindowState), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    private Window window;

    public WindowState WindowState
    {
        get => (WindowState) GetValue(WindowStateProperty);
        set => SetCurrentValue(WindowStateProperty, value);
    }

    protected override void OnAttached()
    {
        base.OnAttached();
        this.window = Window.GetWindow(this.AssociatedObject)!;
        this.window.StateChanged += this.OnStateChanged;
    }

    private void OnStateChanged(object sender, EventArgs e) => this.WindowState = this.window.WindowState;
}

This behavior can be used in any UserControl like this with bound WindowState in ViewModel.

<UserControl x:Class="RCBase.WPF.Monitor.CustomUserView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
         xmlns:behaviors="clr-namespace:RCBase.WPF.Behaviors"
         mc:Ignorable="d" d:DataContext="{d:DesignInstance monitor:CustomUserViewModel}">
<i:Interaction.Behaviors>
    <behaviors:WindowStateBehavior WindowState="{Binding WindowState}" />
</i:Interaction.Behaviors>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文