I am using Fody.PropertyChanged to try and reduce some of the boilerplate code in my WPF project.
When I tried to create a button click event, it appeared that Fody would not handle this in the same way that it handled the binding between, for example, a label and a string property.
I have a textbox and I want a button to be enabled / disabled depending on whether this textbox has text. My XAML looks like:
<Window x:Class="MyProject.View.MainWindow"
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:MyProject"
mc:Ignorable="d">
<Label Content="Enter text:" HorizontalAlignment="Left" Margin="21,43,0,0" VerticalAlignment="Top"/>
<TextBox Height="100"
TextWrapping="Wrap"
Text="{Binding Test}"
VerticalAlignment="Top"
Padding="5, 3, 1, 1"
AcceptsReturn="True" Margin="161,10,10,0"/>
<Button Content="Go"
IsEnabled="{Binding Path=GoButtonIsEnabled}"
Command="{Binding Path=ButtonClick}"
HorizontalAlignment="Left"
Margin="64,158,0,0"
VerticalAlignment="Top" Width="75"/>
</Window>
The ViewModel it is bound to is:
class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ButtonCommandBinding ButtonClick { get; set; }
public bool GoButtonIsEnabled => !String.IsNullOrWhiteSpace(Test);
public string Test { get; set; }
public MainWindowViewModel()
{
ButtonClick = new ButtonCommandBinding(OnGoButtonClick);
}
private void OnGoButtonClick()
{
System.Windows.MessageBox.Show("Hello, world!");
}
protected void NotifyPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The ButtonCommandBinding:
public class ButtonCommandBinding : ICommand
{
private readonly Action handler;
private bool isEnabled;
public ButtonCommandBinding(Action handler)
{
this.handler = handler;
}
public bool IsEnabled
{
get { return isEnabled; }
set
{
if (value != isEnabled)
{
isEnabled = value;
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public bool CanExecute(object parameter)
{
return IsEnabled;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (IsEnabled)
{
handler();
}
}
}
I believe the reason that typing in the textbox isn't enabling the button is because the property changed notifier isn't being called.. but I am not calling it because I am using Fody. I had not implemented NotifyPropertyChanged
in my ViewModel until I tried getting to button click working; before implementing the button click this was handled by Fody (when I was just binding a label to text).
So I have two questions:
EDIT: Thanks to ANu for telling me to add the [AlsoNotifyFor(nameof(GoButtonIsEnabled))] attribute. However, the issue I now have is that when I load the textbox with text set in the ViewModel:
public MainWindowViewModel()
{
Test = "Enter text here";
ButtonClick = new ButtonCommandBinding(OnButtonClick);
ButtonClick.IsEnabled = true;
}
...and I run the app and then delete the text from the textbox, the Go button doesn't disable until I click it. After I click it, the action doesn't run (as it shouldn't, because textbox is empty) and the button disabled. When I enter text into the textbox, it does not re-enable.
It seems the binding still isn't working properly.
public bool GoButtonIsEnabled => !String.IsNullOrWhiteSpace(Test);
[AlsoNotifyFor(nameof(GoButtonIsEnabled))]
public string Test { get; set; }
public MainWindowViewModel()
{
Test = "Enter text";
ButtonClick = new ButtonCommandBinding(OnButtonClick);
ButtonClick.IsEnabled = true;
}
XAML:
<Button Content="Go"
IsEnabled="{Binding Path=GoButtonIsEnabled}"
Command="{Binding Path=ButtonClick}"
HorizontalAlignment="Left"
Margin="64,158,0,0"
VerticalAlignment="Top" Width="75"/>
Fody would inject notify code for each Property as you desired. However, you also want to Notify a depended property(GoButtonIsEnabled
) when another property(Text
) is changed..
You would need to use AlsoNotifyFor attribute to allows the injection of notify code that points to a different property.
[AlsoNotifyFor(nameof(GoButtonIsEnabled ))]
public string Test { get; set; }