【问题标题】:wpf Button always disabled (with CommandBinding, CanExecute=True and IsEnabled= True)wpf 按钮始终禁用(使用 CommandBinding、CanExecute=True 和 IsEnabled=True)
【发布时间】:2021-07-24 16:49:03
【问题描述】:

修改:很抱歉在第一版中遗漏了一些重要的描述,现在问题应该已经明确了:

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

  1. MainWindow.xaml
  2. CustomizedUserControl.xaml

CustomizedUserControl 是 MainWindow 中的一个 Tab,它的 DataContext 在 MainWindow.xaml 中定义为:

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

CustomizedUserControl.xaml 提供了一个画布和一个按钮,因此当按下按钮时,用户应该能够在画布上进行绘制。如下代码所示,Canvas的内容是由dataContext“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) => { };

}

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

因此,我认为命令的数据绑定也可以。 IsEnabled 已设置为 true,并且 CommandBinding 的 CanExecute 只会返回 true。

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

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

感谢您的宝贵时间!希望有人能帮助我,因为我的想法和参考资料已经用完了。

【问题讨论】:

  • 您将 CommanBinding 添加到 _drawableTab 元素的集合中。但是从你的代码中并不清楚这个元素是什么。
  • _drawable 是 CustomizedUserControl ,因为我想设置我的 UserControl 的 CommandBindings 属性,这样当按下 UserControl 中的 Button 时,用户可以在同样位于该 UserControl 的画布上画线。

标签: wpf button data-binding commandbinding routed-commands


【解决方案1】:

做了一个最简单的例子。
一切正常。
BaseInpc 是我在此处的简单 INotifyPropertyChanged 实现:BaseInpc

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>

【讨论】:

    【解决方案2】:

    如果您完整地展示了您的代码,那么我会看到以下问题:

    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 中更易于使用:
        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 在您的代码中也不可见。 如果它确实不存在,那么绑定也不会意识到更改属性值。

    【讨论】:

    • 感谢您的详细解答。我想为我的问题中提供的不清楚的信息道歉。 1. 正如您在我更新的问题中看到的那样,您提到的每个类都使用了一个外部库 Fody/PropertyChanged,因此无需进一步编程即可注入属性通知。我认为 PropertyChanged 设置得很好,证据是我更改了 CADDrawingCommands 的属性“Button1Name”,并且更改已应用于我的按钮,这意味着会引发更改通知。
    • 使用 Fody 可以消除所有关于提出 PropertyChanged 的问题。
    • 使用静态 RoutedCommand 实例是典型的 WPF 方式。 WPF 中的所有默认可路由命令也实现为唯一的静态实例:ApplicationCommands、EditingCommands、ComponentCommands、MediaCommands、NavigationCommands。此外,在 UI 控件的内部逻辑中需要命令的地方,它们也被实现为静态实例。示例:在 ScrollBar 类中,几乎有两打静态 RoutedCommands。
    • 我在您显示的代码中看不到任何明显的问题。我将尝试重复您的代码并重现问题。我会通知你结果。
    • 试图重现您的示例并遇到一个歧义。你声明了class CustomizedUserControl,但在构造函数中你有一个不同的名字public FileEditTabUserControl()。请解释这种歧义。
    猜你喜欢
    • 2014-06-26
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多