开发者

Binding the "WindowState" property of a window in WPF using MVVM

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 correc开发者_如何学JAVAt way to bind the "WindowState" property to my ViewModel so that the window minimizes correctly?


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");
            }
        }
    }


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).


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.


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>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜