设置 DockPanel 可见性 属性 后,DockPanel 内按钮的数据绑定命令不起作用

Databinding Command for a Button inside a DockPanel not working when a DockPanel Visibility property is set

我正在尝试将 Button Command 属性 绑定到 ViewModel 中的 ICommand 属性(按钮放置在 DockPanel 中)。在我设置 DockPanel 的可见性 属性 之前,它工作正常:

<DockPanel  Grid.Row="1">
     <Button Content="Read" Command="{Binding ButtonBeginReadCommand}" DockPanel.Dock="Right"/>
     <Button Content="Write" Command="{Binding ButtonBeginWriteCommand}" DockPanel.Dock="Left"/>
</DockPanel>

但是在 DockPanel 添加可见性 属性 之后,事情变得奇怪了(现在按钮不可点击,但可见性正常):

<DockPanel  Grid.Row="1" Visibility="{Binding IsFilenameCorrect, Converter={StaticResource HiddenIfFalse}}">
     <Button Content="Read" Command="{Binding ButtonBeginReadCommand}" DockPanel.Dock="Right"/>
     <Button Content="Write" Command="{Binding ButtonBeginWriteCommand}" DockPanel.Dock="Left"/>
</DockPanel>

我也尝试为按钮命令设置 RelativeSource,但没有帮助:

<DockPanel  Grid.Row="1" Visibility="{Binding IsFilenameCorrect, Converter={StaticResource HiddenIfFalse}}">
     <Button Content="Read" Command="{Binding DataContext.ButtonBeginReadCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" DockPanel.Dock="Right"/>
     <Button Content="Write" Command="{Binding DataContext.ButtonBeginWriteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" DockPanel.Dock="Left"/>
</DockPanel>

DataContext 设置为:

<Window.DataContext>
    <viewModel:MainWindowViewModel/>
</Window.DataContext>

有一部分 MainWindowViewModel class。我使用了自定义 AsyncCommand 实现(不记得在哪里找到它):

...
public ICommand ButtonBeginReadCommand { get; private set; }
public MainWindowViewModel() {
...
ButtonBeginReadCommand = new AsyncCommand(async () =>
        {

            await Task.Delay(300);
            Monitor.Enter(_locker);
            ...
            Monitor.Exit(_locker);
        });

我该如何解决这个问题?

尝试使用内置 BooleanToVisibilityConverter

我正在分享示例代码。您可能必须更改名称空间才能使其正常工作。

XAML:

<Window x:Class="DockPanel.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:viewModel="clr-namespace:DockPanel"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <viewModel:VM/>
    </Window.DataContext>
    <Window.Resources>
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Window.Resources>
    <DockPanel  Grid.Row="1" Visibility="{Binding IsFilenameCorrect, Converter={StaticResource BooleanToVisibilityConverter}}">
        <Button Content="Read" Command="{Binding DataContext.ButtonBeginReadCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" DockPanel.Dock="Right"/>
        <Button Content="Write" Command="{Binding DataContext.ButtonBeginWriteCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" DockPanel.Dock="Left"/>
    </DockPanel>
</Window>

隐藏代码:

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace DockPanel
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }

    public class VM
    {
        public bool IsFilenameCorrect { get; set; }
        public ICommand ButtonBeginReadCommand { get; set; }
        public ICommand ButtonBeginWriteCommand { get; set; }
        private object _locker = new object();

        public VM()
        {

            IsFilenameCorrect = true;
            ButtonBeginReadCommand = new AsyncCommand(async () =>
                    {
                        await Task.Delay(300);
                        Monitor.Enter(_locker);
                        MessageBox.Show("Read");
                        Monitor.Exit(_locker);
                    });

            ButtonBeginWriteCommand = new AsyncCommand(async () =>
            {
                await Task.Delay(300);
                Monitor.Enter(_locker);
                MessageBox.Show("Write");
                Monitor.Exit(_locker);
            });
        }
    }

    public interface IAsyncCommand : ICommand
    {
        Task ExecuteAsync(object parameter);
    }

    public abstract class AsyncCommandBase : IAsyncCommand
    {
        public abstract bool CanExecute(object parameter);
        public abstract Task ExecuteAsync(object parameter);
        public async void Execute(object parameter)
        {
            await ExecuteAsync(parameter);
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        protected void RaiseCanExecuteChanged()
        {
            CommandManager.InvalidateRequerySuggested();
        }
    }

    public class AsyncCommand : AsyncCommandBase
    {
        private readonly Func<Task> _command;
        public AsyncCommand(Func<Task> command)
        {
            _command = command;
        }
        public override bool CanExecute(object parameter)
        {
            return true;
        }
        public override Task ExecuteAsync(object parameter)
        {
            return _command();
        }
    }
}