簡體   English   中英

C# MVVM RelayCommand CanExecute 操作不會更新按鈕啟用/禁用狀態

[英]C# MVVM RelayCommand CanExecute action does not update button enable/disable state

我是 MVVM 的新手,所以對我一無所知。

目標:

  1. 當我單擊“啟動服務器”按鈕時啟用“停止服務器”按鈕,反之亦然
  2. 使 MainViewModel 中的 IsRunning-property 監聽 _serverModel.IsRunning 屬性中的更改

當前問題:當我單擊“啟動服務器”按鈕時,兩個按鈕都被禁用。

在此處輸入圖片說明

主窗口.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 此外,您應該從StartServerCommandStopServerCommand通知更改

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM