wpf 按钮始终禁用(使用 CommandBinding、CanExecute=True 和 IsEnabled=True)
wpf Button always disabled (with CommandBinding, CanExecute=True and IsEnabled= True)
修正:第一个版本遗漏了一些重要的描述,现在问题应该是well-defined:
所以我正在制作一个具有以下视图的玩具 CAD 程序:
- MainWindow.xaml
- 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 的数据上下文设置为它自己的代码,按钮将是可点击的!为什么?
感谢您的宝贵时间!希望有人能帮助我,因为我 运行 没有想法和参考资料。
如果您完整地展示了您的代码,那么我发现其中存在以下问题:
- 您为 DrawingCommands 属性 设置的值不正确。
在此 属性 中,您不会引发 PropertyChanged。
Button 中的绑定在
InitializeComponent()
方法中初始化。此时属性为空,给它赋值时,绑定查不到
有两种方法可以解决这个问题:
- 在 属性;
中提高 PropertyChanged
- 如果您在构造函数中设置了一次 属性 值,则立即在初始化程序中设置它。将 属性 设置为“只读”。这种方式,在我看来,更好。
public CADDrawingCommands DrawingCommands { get; }
public FileEditTabUserControl()
{
DrawingCommands = new CADDrawingCommands(this);
InitializeComponent();
DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
}
- 您有一个按钮绑定到
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>
- 在 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>
修正:第一个版本遗漏了一些重要的描述,现在问题应该是well-defined:
所以我正在制作一个具有以下视图的玩具 CAD 程序:
- MainWindow.xaml
- 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 的数据上下文设置为它自己的代码,按钮将是可点击的!为什么?
感谢您的宝贵时间!希望有人能帮助我,因为我 运行 没有想法和参考资料。
如果您完整地展示了您的代码,那么我发现其中存在以下问题:
- 您为 DrawingCommands 属性 设置的值不正确。
在此 属性 中,您不会引发 PropertyChanged。
Button 中的绑定在
InitializeComponent()
方法中初始化。此时属性为空,给它赋值时,绑定查不到
有两种方法可以解决这个问题:
- 在 属性; 中提高 PropertyChanged
- 如果您在构造函数中设置了一次 属性 值,则立即在初始化程序中设置它。将 属性 设置为“只读”。这种方式,在我看来,更好。
public CADDrawingCommands DrawingCommands { get; }
public FileEditTabUserControl()
{
DrawingCommands = new CADDrawingCommands(this);
InitializeComponent();
DrawingCommands.Button1Name = "yeahjojo"; //For testing data binding
}
- 您有一个按钮绑定到
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>
- 在 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>