wpfbuttondata-bindingcommandbindingrouted-commands

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


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.


Solution

  • 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>