[英]C# MVVM RelayCommand CanExecute action does not update button enable/disable state
我是 MVVM 的新手,所以对我一无所知。
目标:
当前问题:当我单击“启动服务器”按钮时,两个按钮都被禁用。
主窗口.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>
主窗口.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();
}
}
}
基础视图模型.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;
}
}
}
主视图模型.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;
}
}
服务器模型.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));
}
}
}
中继命令.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);
}
}
如果你在 line 上放置一个断点
set => SetProperty(_serverModel.IsRunning, value);
然后调试程序,您将看到当OnStartServer
IsRunning
属性设置为 true 时,它不会设置_serverModel.IsRunning
因为SetProperty
不会按您的预期更新该字段。 我建议您通过将ref
添加到field
参数来修改SetProperty
方法,如下所示:
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;
}
然后,在MainViewModel
,您只需要根据_serverModel.IsRunning
更新IsRunning
。 此外,您应该从StartServerCommand
和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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.