For some reason, I can't activate my Button.
In my app, I have a TextBox, that gets populated with the file path that the user chooses from a dialog.
I also have a ComboBox to let the user choose a language.
What I want to do, is to activate my button Start, when the FilePath is not empty and the user has selected a language.
[AddINotifyPropertyChangedInterface]
public class AudioPageViewModel {
public string? FilePath { get; set; }
public bool IsWorking { get; set; }
public bool CanPress { get; set; }
public string? SelectedItem { get; set; }
public List<string>? Languages { get; set; }
public Visibility CanShow { get; set; }
public DialogHelper Dialog { get; }
public Command PickFileCommad { get; set; }
public Command StartCommand { get; set; }
public AudioPageViewModel() {
InitListLanguages();
Dialog = new DialogHelper();
CanShow = Visibility.Hidden;
PickFileCommad = new Command(PickFileAction);
StartCommand = new Command(StartAction, CanStartAction);
}
private bool CanStartAction(object arg) {
if (string.IsNullOrEmpty(SelectedItem) ||
string.IsNullOrEmpty(FilePath)) {
return false;
}
return true;
}
private void StartAction(object obj) {
throw new NotImplementedException();
}
private void PickFileAction() {
var filePath = Dialog.GetFilePath(ConstantsHelpers.AUDIO);
FilePath = filePath;
}
private void InitListLanguages() {
Languages = new List<string>() {
"English",
"Spanish"
};
<Grid Margin="20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button
VerticalAlignment="Top"
Command="{Binding PickFileCommad}"
Content="Pick a file"
FontWeight="Bold" />
<Label
Grid.Row="1"
Margin="0,5,0,0"
VerticalContentAlignment="Top"
Content="language of the file" />
<TextBox
Grid.Column="1"
Margin="10,0,10,0"
VerticalAlignment="Top"
IsReadOnly="True"
Text="{Binding FilePath}" />
<ComboBox
Grid.Row="1"
Grid.Column="1"
Margin="10,0,10,0"
SelectedItem="{Binding SelectedItem}"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Languages}">
</ComboBox>
<Label
Grid.Row="3"
Grid.ColumnSpan="2"
Margin="10,0,10,0"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Content="This will not take long"
Visibility="{Binding CanShow}" />
<ui:ProgressRing
Grid.Row="2"
Grid.ColumnSpan="2"
Width="60"
Height="60"
Margin="10,0,10,0"
IsActive="{Binding IsWorking}" />
<Button
Grid.Row="4"
Grid.ColumnSpan="2"
Margin="10,0,10,10"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Content="Start"
Command="{Binding StartCommand}"
IsEnabled="{Binding CanPress}" />
</Grid>
A key issue is that you use both a
Commandand anIsEnabledbinding. You should only use one, as they interfere with each other. A command binding will set the enabled state of a button depending on the result of itsCanExceutemethod. BindingIsEnabledtoo, will override this. I would suggest to use commands only and move the logic fromCanPressto theCanStartActionmethod ofStartCommand.Using Commands
Commands signal if they can be executed by their
CanExecutemethod result. However, there are different implementations of commands that trigger the reevaluation of this method differently.Relay Command With Command Manager
If you use a command implementation that uses the
RequerySuggestedevent, e.g.:Then removing the
IsEnabledbinding is enoungh, as it is triggered by input in the user interface.Relay Command With Explicit Method
The other variant is a command implementation, where a method is exposed to explicitly reevalute its state, e.g.
RaiseCanExcuteChanged. In this case, theCanExecuteChangedevent is raised explicity and the UI element that binds the command is triggered to executeCanExecuteagain and update its own enabled state.Unfortunately, the Fody PropertyChanged library does not support attributes for commands that would trigger reevaluation of can execute. There was already a request, but it was declined and they will not implement it.
A workaround is to create
On<Property>Changedmethods, which will be called by Fody on changes of the corresponding<Property>automatically, see On_PropertyName_Changed in the Wiki. These methods then call the explicit reevaluation method.Using IsEnabled
When using
IsEnabledonly, you have to move theCanStartActionlogic to be executed inCanPress. In this case this would be an explicit implentation that cannot use Fody.Additionally, you could not use a command, so a
Clickhandler would be an ugly alternative. This is why I strongly recommend you to use commands instead.