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

728 Views Asked by At

Revised: I apologize for missing some important descriptions in the first version, now the problem should be well-defined:

so I'm making a toy CAD program with following views:

  1. MainWindow.xaml
  2. CustomizedUserControl.xaml

CustomizedUserControl is a Tab within MainWindow, and its DataContext is defined in MainWindow.xaml as:

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

And CustomizedUserControl.xaml provides a canvas and a button, so when the button is pressed the user should be able to draw on the canvas. As the following code shows, the content of Canvas is prepared by the 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>

It is also notable that I used an external library, Fody/PropertyChanged, in all classes so property notifications would be injected without further programming.

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) => { };

}

The Content of Button is set correctly, as I can read the string defined in Button1Name:

enter image description here

Therefore I suppose the Data Binding for Command is also ok. IsEnabled has been set to true and CanExecute of the CommandBinding would only return true.

Why is my button still greyed out and not clickable?

If I define the button inside a Window instead of UserControl (and set the datacontext of the Window to its own code behind, the button will be clickable! Why?

Thank you for your time! Hopefully would somebody help me cuz I've run out of ideas and references.

2

There are 2 best solutions below

0
On BEST ANSWER

Made the simplest example.
Everything works as it should.
BaseInpc is my simple INotifyPropertyChanged implementation from here: 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>
8
On

If you showed your code in full, then I see the following problems in it:

  1. You are setting the value incorrectly for the DrawingCommands property. In this property, you do not raise PropertyChanged. The binding in the Button is initialized in the InitializeComponent() method. At this point, the property is empty, and when you set a value to it, the binding cannot find out.

There are two ways to fix this:

  • Raise PropertyChanged in the property;
  • If you set the property value once in the constructor, then set it immediately in the initializer. Make the property "Read Only". This way, in my opinion, is better.
  public CADDrawingCommands DrawingCommands { get; }
  public FileEditTabUserControl()
  {
      DrawingCommands = new CADDrawingCommands(this);

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

  1. You have a button bound to a command in the DrawingCommands.LinesChainCommand property. But to this property, you assign an empty instance of the = new RoutedCommand () routing command. This looks pointless enough. If you need a routable command, create it in the "Read Only" static property. This will make it much easier to use in 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. Raising PropertyChanged in CADDrawingCommands properties is also not visible in your code. If it really does not exist, then the binding is also unaware of changing property values.