Close a window via MVVM

Eduardo Gomez 3,651 Reputation points
2021-05-13T16:43:14.697+00:00

Hello

I am studying MVVM, and I manage to activate a button based on the fields, but I am trying to close the current Window. One thing I was searching is to send a Window to the ViewModel but that is not a very good Idea

This is my VM

 public ICommand LoginCommand { get; private set; }

        private string _Username;
        public string Username {
            get { return _Username; }
            set {
                if (_Username != value) {
                    _Username = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged("User");
                }
            }
        }


        private string _Password;
        public string Password {
            get { return _Password; }
            set {
                if (_Password != value) {
                    _Password = value;
                    RaisePropertyChanged();
                    RaisePropertyChanged("User");
                }
            }
        }


        public LoginVM() {
            LoginCommand = new HelperCommand {
                ExecuteDelegate = x => RegisterUser(),
                CanExecuteDelegate = x => CanRegister()
            };
        }

        private void RegisterUser() {
            NotesWindow notesWindow = new();
            notesWindow.Show();
             //Close current windows
        }

        private bool CanRegister() {

            if (!string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password)) {
                return true;
            }
            return false;

        }

this is my CommandHelper

public class HelperCommand : ICommand {
    public Predicate<object> CanExecuteDelegate { get; set; }
    public Action<object> ExecuteDelegate { get; set; }

    #region ICommand Members

    public bool CanExecute(object parameter) {
        if (CanExecuteDelegate != null)
            return CanExecuteDelegate(parameter);
        return true;// if there is no can execute default to true
    }

    public event EventHandler CanExecuteChanged {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter) {
        if (ExecuteDelegate != null) {
            ExecuteDelegate(parameter);
        }

 #endregion
    }
}

this is my UI

<Window.DataContext>
<vm:LoginVM />
</Window.DataContext>

<StackPanel Margin="20,40,20,20">
    <inputLayout:SfTextInputLayout x:Name="sfTextInputLayout"
                                   Hint="Username"
                                   Margin="20,20,20,0">
        <TextBox Foreground="BlanchedAlmond"
                 Text="{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    </inputLayout:SfTextInputLayout>
    <controls:CustomPasswordControl Password="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <Button Style="{StaticResource MyBtnStyle}"
            Margin="80,100,80,0"
            Height="30"
            Content="Sign in"
            Command="{Binding LoginCommand}"
            CommandParameter="{Binding ElementName=window, Mode=OneWay}">
    </Button>
</StackPanel>

</Window>

Developer technologies | Windows Presentation Foundation
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2021-05-14T05:04:55.61+00:00

    Hi,
    please, use another HelperCommand and your code work. Try following demo:

    using System;
    using System.Windows;
    using System.Windows.Input;
    
    namespace WpfApp055
    {
      public class ViewModel
      {
        public ICommand LoginCommand { get => new HelperCommand(RegisterUser, CanRegister); }
        private void RegisterUser(object parameter)
        {
          Window wnd = parameter as Window;
          wnd?.Close();
        }
    
        private bool CanRegister(object parameter) => true;
      }
    
      public class HelperCommand : ICommand
      {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;
        public HelperCommand(Action<object> execute) : this(execute, canExecute: null) { }
        public HelperCommand(Action<object> execute, Predicate<object> canExecute)
        {
          if (execute == null) throw new ArgumentNullException("execute");
          this._execute = execute;
          this._canExecute = canExecute;
        }
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter) => this._canExecute == null ? true : this._canExecute(parameter);
        public void Execute(object parameter) => this._execute(parameter);
        public void RaiseCanExecuteChanged() => this.CanExecuteChanged?.Invoke(this, EventArgs.Empty);
      }
    }
    

    And XAML:

    <Window x:Class="WpfApp1.Window055"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:WpfApp055"
            mc:Ignorable="d"
            Title="Close Window" Height="450" Width="800"
            x:Name="window">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <StackPanel>
        <Button Content="Close Window"
                Command="{Binding LoginCommand}"
                CommandParameter="{Binding ElementName=window, Mode=OneWay}"/>
      </StackPanel>
    </Window>
    
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.