Reputation: 1031
I am new to MVVM so bare with me.
Objectives:
Current issue: When I click "Start Server"-button, both buttons become disabled.
MainWindow.xaml
<Window x:Class="ServerGUI.MainWindow"
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:uc="clr-namespace:ServerGUI.Views"
xmlns:vm="clr-namespace:ServerGUI.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="150" Width="200">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<GroupBox Grid.Column="0" Header="Server Properties">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="30" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Start Server" Command="{Binding Path=StartServerCommand}" Margin="3 5 3 0" />
<Button Grid.Row="1" Content="Stop Server" Command="{Binding Path=StopServerCommand}" Margin="3 5 3 0" />
<Label Grid.Row="2" x:Name="lblServerStatus" Content="Server Status: Offline" />
</Grid>
</GroupBox>
</Grid>
</Window>
MainWindow.xaml.cs
using ServerGUI.ViewModels;
using System.Windows;
namespace ServerGUI
{
public partial class MainWindow : Window
{
public MainViewModel MainViewModel { get; set; }
public MainWindow()
{
MainViewModel = new MainViewModel();
DataContext = MainViewModel;
InitializeComponent();
}
}
}
BaseViewModel.cs
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ServerGUI.ViewModels
{
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
}
}
MainViewModel.cs
using ServerGUI.Commands;
using ServerGUI.Models;
using System.Windows.Input;
namespace ServerGUI.ViewModels
{
public class MainViewModel : BaseViewModel
{
private readonly ServerModel _serverModel;
public bool IsRunning
{
get => _serverModel.IsRunning;
set => SetProperty(_serverModel.IsRunning, value);
}
private readonly RelayCommand _startServerCommand;
private readonly RelayCommand _stopServerCommand;
public ICommand StartServerCommand => _startServerCommand;
public ICommand StopServerCommand => _stopServerCommand;
public MainViewModel()
{
_serverModel = new ServerModel();
_startServerCommand = new RelayCommand(OnStartServer, CanStartServer);
_stopServerCommand = new RelayCommand(OnStopServer, CanStopServer);
}
private void OnStartServer(object command)
{
IsRunning = true;
_startServerCommand.InvokeCanExecuteChanged();
}
private void OnStopServer(object command)
{
IsRunning = false;
_stopServerCommand.InvokeCanExecuteChanged();
}
private bool CanStartServer(object command) => !IsRunning;
private bool CanStopServer(object command) => IsRunning;
}
}
ServerModel.cs
using System.ComponentModel;
namespace ServerGUI.Models
{
public class ServerModel : INotifyPropertyChanged
{
private bool isRunning = default;
public event PropertyChangedEventHandler PropertyChanged;
public ServerModel()
{
IsRunning = false;
}
public bool IsRunning
{
get => isRunning;
set
{
isRunning = value;
RaisePropertyChanged(nameof(IsRunning));
}
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
RelayCommand.cs
using System;
using System.Windows.Input;
namespace ServerGUI.Commands
{
class RelayCommand : ICommand
{
private readonly Action<object> _executeAction;
private readonly Func<object, bool> _canExecuteAction;
public RelayCommand(Action<object> executeAction, Func<object, bool> canExecuteAction)
{
_executeAction = executeAction;
_canExecuteAction = canExecuteAction;
}
public void Execute(object parameter) => _executeAction(parameter);
public bool CanExecute(object parameter) => _canExecuteAction?.Invoke(parameter) ?? true;
public event EventHandler CanExecuteChanged;
public void InvokeCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
Upvotes: 1
Views: 1098
Reputation: 748
If you put a break point at line
set => SetProperty(_serverModel.IsRunning, value);
then debug the program, you will see that when property IsRunning
is set to true in OnStartServer
, it does not set _serverModel.IsRunning
because SetProperty
does not update the field as you expected.
I would suggest you to modify SetProperty
method by adding ref
to field
parameter as follows:
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
Then, in MainViewModel
, you just need to update IsRunning
based on _serverModel.IsRunning
. Also, you should notify changes from both StartServerCommand
and StopServerCommand
public class MainViewModel : BaseViewModel
{
private readonly ServerModel _serverModel;
public bool IsRunning
{
get => _serverModel.IsRunning;
}
private readonly RelayCommand _startServerCommand;
private readonly RelayCommand _stopServerCommand;
public ICommand StartServerCommand => _startServerCommand;
public ICommand StopServerCommand => _stopServerCommand;
public MainViewModel()
{
_serverModel = new ServerModel();
_startServerCommand = new RelayCommand(OnStartServer, CanStartServer);
_stopServerCommand = new RelayCommand(OnStopServer, CanStopServer);
}
private void OnStartServer(object command)
{
_serverModel.IsRunning = true;
_startServerCommand.InvokeCanExecuteChanged();
_stopServerCommand.InvokeCanExecuteChanged();
}
private void OnStopServer(object command)
{
_serverModel.IsRunning = false;
_startServerCommand.InvokeCanExecuteChanged();
_stopServerCommand.InvokeCanExecuteChanged();
}
private bool CanStartServer(object command) => !IsRunning;
private bool CanStopServer(object command) => IsRunning;
}
Upvotes: 1