SubmarineX
SubmarineX

Reputation: 850

How can I resize, minimize, maximize or close Window in the MVVM WPF?

I want to create a Metro style Window, hide original Border, so I need resize Window (ResizeWindow but he doesn't use MVVM) and create some Buttons to minimize, maximize and close Window, With MVVM. But there're not SourceInitialized, Cursor, WindowStatus, etc. I know these are Window's.

There're 3 correlative files:

MainWindow.xaml
MainWindow.xaml.cs
MainViewModel.cs

My questions

  1. Do I need resize, minimize, maximize or close Window in ViewModel?
  2. If so, how can I achieve this?
  3. If not, whether it violate MVVM pattern?

Update

After a night, I find that I unnecessarily consider the ClickEvent of the CloaseButton, just call this.Close() is OK. If need to do sth. after click ColoseButton(or close app), just call the ClosedEvent or the ClosingEvent of the Window.

Upvotes: 4

Views: 5967

Answers (2)

Anatoliy Nikolaev
Anatoliy Nikolaev

Reputation: 22702

I can offer the following: leave operations that are done from View on the side of View. Operation with the Window should be placed in a special class WindowBehaviours that would be available from XAML, like that:

Operation Close:

<Trigger Property="IsChecked" Value="True">
    <Setter Property="Controls:WindowBehaviours.Close" Value="True" />
</Trigger>

Operation Hide:

<Trigger Property="IsChecked" Value="True">
    <Setter Property="Controls:WindowBehaviours.Hide" Value="True" />
</Trigger>

This uses ToggleButton because he has property IsChecked accessible via a trigger. These properties - attached depending properties.

Listing of WindowBehaviours class:

public static class WindowBehaviours
{
    // Close the Window
    public static void SetClose(DependencyObject target, bool value)
    {
        target.SetValue(CloseProperty, value);
    }

    public static readonly DependencyProperty CloseProperty =
                                              DependencyProperty.RegisterAttached("Close",
                                              typeof(bool),
                                              typeof(WindowBehaviours),
                                              new UIPropertyMetadata(false, OnClose));

    private static void OnClose(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool && ((bool)e.NewValue))
        {
            Window window = GetWindow(sender);

            if (window != null)
            {
                window.Close();
            }
        }
    }

    // Hide the Window
    public static void SetHide(DependencyObject target, bool value)
    {
        target.SetValue(HideProperty, value);
    }

    public static readonly DependencyProperty HideProperty =
                                              DependencyProperty.RegisterAttached("Hide",
                                              typeof(bool),
                                              typeof(WindowBehaviours),
                                              new UIPropertyMetadata(false, OnHide));

    private static void OnHide(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool && ((bool)e.NewValue))
        {
            Window window = GetWindow(sender);

            if (window != null)
            {
                window.WindowState = WindowState.Minimized;
            }
        }
    }

    // Full the Window
    public static void SetFull(DependencyObject target, bool value)
    {
        target.SetValue(FullProperty, value);
    }

    public static readonly DependencyProperty FullProperty =
                                              DependencyProperty.RegisterAttached("Full",
                                              typeof(bool),
                                              typeof(WindowBehaviours),
                                              new UIPropertyMetadata(false, OnFull));

    private static void OnFull(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool && ((bool)e.NewValue))
        {
            Window window = GetWindow(sender);

            if (window != null)
            {
                window.WindowState = WindowState.Maximized;
            }
        }
    }

    // Set the Window in Normal
    public static void SetNormal(DependencyObject target, bool value)
    {
        target.SetValue(NormalProperty, value);
    }

    public static readonly DependencyProperty NormalProperty =
                                              DependencyProperty.RegisterAttached("Normal",
                                              typeof(bool),
                                              typeof(WindowBehaviours),
                                              new UIPropertyMetadata(false, OnNormal));

    private static void OnNormal(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is bool && ((bool)e.NewValue))
        {
            Window window = GetWindow(sender);

            if (window != null)
            {
                window.WindowState = WindowState.Normal;
            }
        }
    }

    // Get the Window
    private static Window GetWindow(DependencyObject sender)
    {
        Window window = null;

        if (sender is Window)
        {
            window = (Window)sender;
        }

        if (window == null)
        {
            window = Window.GetWindow(sender);
        }

        return window;
    }
}

Samples of ToggleButtons:

CloseButton

<Style x:Key="ToggleButtonWindowClose" TargetType="{x:Type ToggleButton}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="SnapsToDevicePixels" Value="True" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">                           
                <Grid>
                    <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />                                    
                    <Border x:Name="BorderClose" Background="Beige" BorderThickness="0" Width="22" Height="22" HorizontalAlignment="Right" Margin="0,6,8,0" VerticalAlignment="Top" Opacity="0" />
                    <Path x:Name="CloseWindow" SnapsToDevicePixels="True" ToolTip="Close window" Width="18" Height="17" Margin="0,0,10,0" HorizontalAlignment="Right" VerticalAlignment="Center" Stretch="Fill" Fill="#2D2D2D" Data="F1 M 26.9166,22.1667L 37.9999,33.25L 49.0832,22.1668L 53.8332,26.9168L 42.7499,38L 53.8332,49.0834L 49.0833,53.8334L 37.9999,42.75L 26.9166,53.8334L 22.1666,49.0833L 33.25,38L 22.1667,26.9167L 26.9166,22.1667 Z " />
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="CloseWindow" Property="Fill" Value="#C10000" />
                        <Setter TargetName="BorderClose" Property="Opacity" Value="1" />
                    </Trigger>

                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Controls:WindowBehaviours.Close" Value="True" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>            

HideButton

<Style x:Key="ButtonWindowHide" TargetType="{x:Type ToggleButton}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="SnapsToDevicePixels" Value="True" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid>
                    <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                    <Border x:Name="BorderHide" Background="Beige" BorderThickness="0" Width="22" Height="22" HorizontalAlignment="Right" Margin="0,6,32,0" VerticalAlignment="Top" Opacity="0" />
                    <Path x:Name="HideWindow" SnapsToDevicePixels="True" ToolTip="Hide window" Width="14" Height="19" Margin="0,0,36,0" HorizontalAlignment="Right" VerticalAlignment="Center" Stretch="Fill" Fill="#2D2D2D" Data="F1 M 42,19.0002L 34,19.0002L 34,43.7502L 24,33.7502L 24,44.2502L 38,58.2502L 52,44.2502L 52,33.7502L 42,43.7502L 42,19.0002 Z " />
                </Grid>

                <ControlTemplate.Triggers>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter TargetName="HideWindow" Property="Fill" Value="#0094FF" />
                        <Setter TargetName="BorderHide" Property="Opacity" Value="1" />
                    </Trigger>

                    <Trigger Property="IsChecked" Value="True">
                        <Setter Property="Controls:WindowBehaviours.Hide" Value="True" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

FullScreenButton

<Style x:Key="ButtonWindowFull" TargetType="{x:Type ToggleButton}">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="SnapsToDevicePixels" Value="True" />

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Grid>
                    <ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" />
                    <Border x:Name="BorderFull" Background="Beige" BorderThickness="0" Width="22" Height="22" HorizontalAlignment="Right" Margin="0,6,58,0" VerticalAlignment="Top" Opacity="0" />
                    <Path x:Name="FullWindow" SnapsToDevicePixels="True" Width="19" Height="19" Margin="0,0,60,0" HorizontalAlignment="Right" VerticalAlignment="Center" Stretch="Fill" Fill="#2D2D2D" Data="F1 M 30.25,58L 18,58L 18,45.75L 22,41.75L 22,50.75L 30,42.75L 33.25,46L 25.25,54L 34.25,54L 30.25,58 Z M 58,45.75L 58,58L 45.75,58L 41.75,54L 50.75,54L 42.75,46L 46,42.75L 54,50.75L 54,41.75L 58,45.75 Z M 45.75,18L 58,18L 58,30.25L 54,34.25L 54,25.25L 46,33.25L 42.75,30L 50.75,22L 41.75,22L 45.75,18 Z M 18,30.25L 18,18L 30.25,18L 34.25,22L 25.25,22L 33.25,30L 30,33.25L 22,25.25L 22,34.25L 18,30.25 Z ">
                    <Path.ToolTip>
                        <ToolTip x:Name="FullWindowToolTip" Content="Full window" />
                    </Path.ToolTip>
                </Path>
            </Grid>

            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter TargetName="FullWindow" Property="Fill" Value="#0094FF" />
                    <Setter TargetName="BorderFull" Property="Opacity" Value="1" />
                </Trigger>

                <Trigger Property="IsChecked" Value="True">
                    <Setter Property="Controls:WindowBehaviours.Full" Value="True" />
                    <Setter TargetName="FullWindow" Property="Data" Value="F1 M 54.2499,34L 42,34L 42,21.7501L 45.9999,17.7501L 45.9999,26.7501L 53.9999,18.7501L 57.2499,22.0001L 49.2499,30.0001L 58.2499,30.0001L 54.2499,34 Z M 34,21.7501L 34,34L 21.75,34L 17.75,30.0001L 26.75,30.0001L 18.75,22.0001L 22,18.7501L 30,26.7501L 30,17.7501L 34,21.7501 Z M 21.75,42L 34,42L 34,54.25L 30,58.25L 30,49.25L 22,57.25L 18.75,54L 26.75,46L 17.75,46L 21.75,42 Z M 42,54.25L 42,42L 54.2499,42L 58.2499,46L 49.2499,46.0001L 57.2499,54L 53.9999,57.25L 45.9999,49.25L 45.9999,58.25L 42,54.25 Z " />
                    <Setter TargetName="FullWindowToolTip" Property="Content" Value="Hide in window" />
                </Trigger>

                <Trigger Property="IsChecked" Value="False">
                    <Setter Property="Controls:WindowBehaviours.Normal" Value="True" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Setter.Value>
</Setter>
</Style>

Output

enter image description here

Resize, minimize, close.

Upvotes: 4

devdigital
devdigital

Reputation: 34349

You might want to consider MahApps.Metro or Modern UI for WPF

Upvotes: 0

Related Questions