0

So I am new to the whole C#/WPF thing and was wondering how to make a button that when clicked will close the application. I have tried numerous suggestions from google searches like:

None of which seemed to help me out properly. Below I have provided the code I currently have that I made following a tutorial on ViewModel navigation. If more code is needed let me know.

Files: File List

MainMenuViewModel:

using Creator.Commands;
using Creator.Stores;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Input;

namespace Creator.ViewModels
{
    class MainMenuViewModel : ViewModelBase
    {
        public ICommand NavigateItemCreateMenuCommand { get; }

        public MainMenuViewModel(NavigationStore navigationStore)
        {
            NavigateItemCreateMenuCommand = new NavigateCommand<ItemCreateMenuViewModel>(navigationStore, () => new ItemCreateMenuViewModel(navigationStore));
        }
    }
}

MainMenuView: Quit button must close the application

<UserControl x:Class="Creator.Views.MainMenuView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Creator.Views"
             mc:Ignorable="d" 
             d:DesignHeight="720" d:DesignWidth="1280">
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>

        <DockPanel Background="LightBlue" Grid.Row="1" Grid.Column="1" >
            <StackPanel>
                <Label Content="Main Menu" HorizontalAlignment="Center" VerticalAlignment="Stretch" FontWeight="Bold" FontFamily="Century Gothic" FontSize="24"/>
                <Button Content="Create Item" FontWeight="Bold" FontFamily="Century Gothic" FontSize="18" Margin="20,0,20,5" Padding="0,5,0,5" Command="{Binding NavigateItemCreateMenuCommand}"/>
                <Button Content="Quit" FontWeight="Bold" FontFamily="Century Gothic" FontSize="18" Margin="20,0,20,5" Padding="0,5,0,5" VerticalAlignment="Bottom"/>
            </StackPanel>
        </DockPanel>
    </Grid>
</UserControl>

MainWindow.xaml.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Creator
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

TL;DR: I need to make the Quit button on MainMenuViewModel/MainMenuView to close the application.

  • It's not quite clear what do you want. Button to close the Application or just a window. You write `MainMenuView` but it's not a `Window`, but a `UserControl`. If application, then just extend your VM with command, where you call `Application.Current.Shutdown();`. – Rekshino Apr 07 '21 at 11:19
  • @Rekshino yes sorry, I meant the app, thought that the Window and App closing is the same. Will update. – Rihards Krastiņš Apr 07 '21 at 11:23
  • the solution using provided here is way to go: https://stackoverflow.com/a/16182065/3873053 – Ugur Apr 07 '21 at 11:34
  • Does this answer your question? [Close Window from ViewModel](https://stackoverflow.com/questions/16172462/close-window-from-viewmodel) – Ugur Apr 07 '21 at 13:15

3 Answers3

1

Extend your ViewModel with QuitCommand and do bind it to the Button.

class MainMenuViewModel : ViewModelBase
{
    public ICommand NavigateItemCreateMenuCommand { get; }
    public ICommand QuitCommand { get; }

    public MainMenuViewModel(NavigationStore navigationStore)
    {
        NavigateItemCreateMenuCommand = new NavigateCommand<ItemCreateMenuViewModel>(navigationStore, () => new ItemCreateMenuViewModel(navigationStore));
        
        QuitCommand = new RelayCommand((par) => { Application.Current.Shutdown(); });
    }
}

<Button Content="Quit" FontWeight="Bold" FontFamily="Century Gothic" FontSize="18" Margin="20,0,20,5" Padding="0,5,0,5" VerticalAlignment="Bottom" Command="{Binding QuitCommand}"/>

Find also implementation of RelayCommand in Why RelayCommand

Clemens
  • 123,504
  • 12
  • 155
  • 268
Rekshino
  • 6,954
  • 2
  • 19
  • 44
1

You should use Command to close a running window in the ViewModel. You can import the currently running MainWindow by handing over the parent control Window to the CommandParameter.

I made a sample for you by using MVVM. I hope it helps you.
Github

Structure

MainMenuView.xaml

<Grid>
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>

        <DockPanel Style="{StaticResource MAIN.DOCK}">
            <StackPanel>
                <Label Style="{StaticResource MAIN.LABEL}"/>
                <Button Style="{StaticResource BTN.CREATE}"/>
                <Button Style="{StaticResource BTN.QUIT}"/>
            </StackPanel>
        </DockPanel>
    </Grid>
</Grid>

MainMenuView.xaml.cs

public partial class MainMenuView : UserControl
{
    public MainMenuView()
    {
        InitializeComponent();
        DataContext = new MainMenuViewModel();
    }
}

MainMenuViewModel

public class MainMenuViewModel
{
    public ICommand QuitCommand { get; set; }

    public MainMenuViewModel()
    { 
        QuitCommand = new RelayCommand<object>(QuitApp);
    }

    private void QuitApp(object obj)
    {
        if (obj is Window window)
        {
            window.Close();
        }
    }
}

MainMenuResource

The whole resource source is in the Github.

<Style TargetType="{x:Type Button}" x:Key="BTN.QUIT">
    <Setter Property="Command" Value="{Binding QuitCommand}"/>
    <Setter Property="CommandParameter" Value="{Binding RelativeSource={RelativeSource AncestorType=Window}}"/>
    <Setter Property="Content" Value="Quit"/>
    <Setter Property="FontWeight" Value="Bold"/>
    <Setter Property="FontFamily" Value="Century Gothic"/>
    <Setter Property="FontSize" Value="18"/>
    <Setter Property="Margin" Value="20 0 20 5"/>
    <Setter Property="Padding" Value="0 5 0 5"/>
    <Setter Property="VerticalAlignment" Value="Bottom"/>
</Style>
elena.kim
  • 930
  • 4
  • 12
  • 22
  • This is closing the Window, but not shutting down the application, if you have other shells opened in the same time, you get problems. – SomeCode.NET Apr 07 '21 at 14:13
  • `window.Close()` I think this is a good way. By default, when you invoke Close() on a Window object that you want to close, events such as Closing and Closed also work sequentially. – jamesnet214 Apr 07 '21 at 14:29
0

First i'll create an interface

public interface IClosable
{
  Action Close { get;set; }
}

then you're viewmodel should implement this interface

MainWindowViewModel : ICloseable
{
 public Action Close { get;set; }
 //your code 
}

then on the window

public partial class MainMenuView : Window
{
    public MainMenuView()
    {
        InitializeComponent();
        DataContext = new MainMenuViewModel();
           
        if(DataContext is IClosable closable)
        {
           closable.Close += this.Close();
        }
    }
}

Also unhook the delegate before closing window. Now invoking Close Action in viewmodel will close window.

Athul Raj
  • 189
  • 1
  • 9