wpf 按钮始终禁用(使用 CommandBinding、CanExecute=True 和 IsEnabled=True)

wpf Button always disabled (with CommandBinding, CanExecute=True and IsEnabled= True)

修正:第一个版本遗漏了一些重要的描述,现在问题应该是well-defined:

所以我正在制作一个具有以下视图的玩具 CAD 程序:

  1. MainWindow.xaml
  2. CustomizedUserControl.xaml

CustomizedUserControl 是 MainWindow 中的一个选项卡,其 DataContext 在 MainWindow.xaml 中定义为:

 <Window.Resources>
        <DataTemplate DataType="{x:Type local:CustomizedTabClass}">
            <local:UserControl1/>
        </DataTemplate>
 </Window.Resources>

和CustomizedUserControl.xaml提供了一个canvas和一个按钮,所以当按下按钮时用户应该能够在canvas上绘图。如以下代码所示,Canvas 的内容由数据上下文“tabs:CustomizedTabClass”准备。

CustomizedUserControl.xaml


<CustomizedUserControl x:Name="Views.CustomizedUserControl11"
...
>
<Button ToolTip="Lines (L)" BorderThickness="2"
                        Command="{Binding ElementName=CustomizedUserControl11, 
                        Path=DrawingCommands.LinesChainCommand}"
                        IsEnabled="True"
                        Content = "{Binding ElementName=CustomizedUserControl11, 
                        Path=DrawingCommands.Button1Name}">
</Button>
...
<canvas x:Name="CADCanvas"
        Drawing="{Binding Drawing ,Mode=TwoWay}" >
</canvas>

同样值得注意的是,我在所有 类 中使用了一个外部库 Fody/PropertyChanged,因此 属性 通知将在没有进一步编程的情况下被注入。

CustomizedUserControl.xaml.cs

using PropertyChanged;
using System.ComponentModel;
using System.Windows.Controls;

[AddINotifyPropertyChangedInterface]
public partial class CustomizedUserControl: Usercontrol, INotifyPropertyChanged{
  public CADDrawingCommands DrawingCommands { get; set; }
  public CustomizedUserControl()
  {
      InitializeComponent();
      DrawingCommands = new CADDrawingCommands(this);
      DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
  }
  public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

}

CADDrawingCommands.cs

using PropertyChanged;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows;

[AddINotifyPropertyChangedInterface]
public class CADDrawingCommands : INotifyPropertyChanged{
  UserControl _drawableTab;
  public string Button1Name { get; set; } = "TestForDataBinding";
  public RoutedCommand LinesChainCommand { get; set; } = new RoutedCommand();
  public CADDrawingCommands(UserControl dTab){
        _drawableTab = dTab;
        CommandBinding lineCommandBinding = new CommandBinding(LinesChainCommand,
             (object sender, ExecutedRoutedEventArgs e) =>
             {
                 MessageBox.Show("Test");
                 //Draw on canvas inside CustomizedUserControl (modify Drawing property in CustomizedTabClass)
             }, (object sender, CanExecuteRoutedEventArgs e) => { e.CanExecute = true; });

            _drawableTab.CommandBindings.Add(lineCommandBinding);       

        }
 public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };

}

按钮的内容设置正确,因为我可以读取 Button1Name 中定义的字符串:

所以我想命令的数据绑定也可以。 IsEnabled 已设置为真,CommandBinding 的 CanExecute 只会 return 真。

为什么我的按钮仍然是灰色且不可点击?

如果我在 Window 而不是 UserControl 中定义按钮(并将 Window 的数据上下文设置为它自己的代码,按钮将是可点击的!为什么?

感谢您的宝贵时间!希望有人能帮助我,因为我 运行 没有想法和参考资料。

如果您完整地展示了您的代码,那么我发现其中存在以下问题:

  1. 您为 DrawingCommands 属性 设置的值不正确。 在此 属性 中,您不会引发 PropertyChanged。 Button 中的绑定在 InitializeComponent() 方法中初始化。此时属性为空,给它赋值时,绑定查不到

有两种方法可以解决这个问题:

  • 在 属性;
  • 中提高 PropertyChanged
  • 如果您在构造函数中设置了一次 属性 值,则立即在初始化程序中设置它。将 属性 设置为“只读”。这种方式,在我看来,更好。
  public CADDrawingCommands DrawingCommands { get; }
  public FileEditTabUserControl()
  {
      DrawingCommands = new CADDrawingCommands(this);

      InitializeComponent();
      DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
  }

  1. 您有一个按钮绑定到 DrawingCommands.LinesChainCommand 属性 中的命令。 但是对于这个 属性,你分配了一个 = new RoutedCommand () 路由命令的空实例。 这看起来毫无意义。 如果您需要可路由命令,请在“只读”静态 属性 中创建它。 这将使它更容易在 XAML:
  2. 中使用
    public static RoutedCommand LinesChainCommand { get; }  = new RoutedCommand();
<Button ToolTip="Lines (L)" BorderThickness="2"
                        Command="{x:Static local:DrawingCommands.LinesChainCommand}"
                        IsEnabled="True"
                        Content = "{Binding ElementName=CustomizedUserControl11, 
                        Path=DrawingCommands.Button1Name}">
</Button>
  1. CADDrawingCommands 属性中提升 PropertyChanged 在您的代码中也不可见。 如果它真的不存在,那么绑定也不知道更改 属性 值。

做了最简单的例子
一切正常。
BaseInpc 是我的简单 INotifyPropertyChanged 实现,来自这里:

using Simplified;
using System.Windows;
using System.Windows.Input;

namespace CustomizedUserControlRoutedCommand
{
    public class CADDrawingCommands : BaseInpc
    {
        UIElement _drawableTab;
        private string _button1Name = "TestForDataBinding";

        public string Button1Name { get => _button1Name; set => Set(ref _button1Name, value); }
        public static RoutedCommand LinesChainCommand { get; } = new RoutedCommand();
        public CADDrawingCommands(UIElement dTab)
        {
            _drawableTab = dTab;
            CommandBinding lineCommandBinding = new CommandBinding(LinesChainCommand,
                 (object sender, ExecutedRoutedEventArgs e) =>
                 {
                     MessageBox.Show("Test");
                     //Draw on canvas inside CustomizedUserControl (modify Drawing property in CustomizedTabClass)
                 }, (object sender, CanExecuteRoutedEventArgs e) => { e.CanExecute = true; });

            _drawableTab.CommandBindings.Add(lineCommandBinding);
        }
    }
}
<UserControl x:Name="CustomizedUserControl11" x:Class="CustomizedUserControlRoutedCommand.CustomizedUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CustomizedUserControlRoutedCommand"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <Button ToolTip="Lines (L)" BorderThickness="2"
                        Command="{x:Static local:CADDrawingCommands.LinesChainCommand}"
                        IsEnabled="True"
                        Content = "{Binding ElementName=CustomizedUserControl11, 
                        Path=DrawingCommands.Button1Name}">
        </Button>
    </Grid>
</UserControl>
using System.Windows.Controls;

namespace CustomizedUserControlRoutedCommand
{
    public partial class CustomizedUserControl : UserControl
    {
        public CADDrawingCommands DrawingCommands { get; }
        public CustomizedUserControl()
        {
            DrawingCommands = new CADDrawingCommands(this);

            InitializeComponent();
            DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
        }
    }
}

<Window x:Class="CustomizedUserControlRoutedCommand.TestCustomizedUserControlWindow"
        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:CustomizedUserControlRoutedCommand"
        mc:Ignorable="d"
        Title="TestCustomizedUserControlWindow" Height="450" Width="800">
    <Grid>
        <local:CustomizedUserControl/>
    </Grid>
</Window>