I have a combo box and a button. I want whenever I choose 1 of the item from it, the button is not disabled
XAML :
<Border Style="{StaticResource borderMain}"
Grid.Row="7"
Grid.Column="0">
<ComboBox ItemsSource="{Binding Source={StaticResource portNames}}"
SelectedItem="{Binding SelectedPort, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
x:Name="Port_Selector" Grid.Column="0"
Text="Port Selector" Background="White"/>
</Border>
<Border Style="{StaticResource borderMain}"
Grid.Column="1"
Grid.Row="7">
<Button Content="Connect"
Command="{Binding OpenPortCommand}"
CommandParameter="{Binding SelectedPort}"
Style="{StaticResource buttonMain}"
Margin="5"/>
</Border>
Command :
public class OpenPortCommand : ICommand
{
public OpenPortVM OpenPortVM{ get; set; }
public OpenPortCommand(OpenPortVM OpenPortVM)
{
this.OpenPortVM = OpenPortVM;
}
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested = value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object? parameter)
{
string? portCom = parameter as string;
if (!string.IsNullOrEmpty(portCom))
return true;
return false;
}
public void Execute(object? parameter)
{
OpenPortVM.ConnectPort();
}
}
I already debug it and check the value from variable that I using for binding SelectedPort
and it has a value on it but somehow, the CommandParameter for my button not detected so CanExecute
method is not run properly. Am i missing something?
Update
Update 2
CodePudding user response:
Your OpenPortCommand
is missing the possibility to actually raise the CanExecuteChanged
event. I suggest following:
public class OpenPortCommand : ICommand
{
public OpenPortVM OpenPortVM{ get; set; }
public OpenPortCommand(OpenPortVM OpenPortVM)
{
this.OpenPortVM = OpenPortVM;
}
// Leave this event as is, don't do an explicit implementation
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChange() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public bool CanExecute(object? parameter) => !string.IsNullOrEmpty(parameter as string);
// rest ommitted
}
In your ViewModel's SelectedPort
property:
public string SelectedPort
{
get => _selectedPort;
set
{
_selectedPort = value;
// call your OnPropertyChanged( ... );
OpenPortCommand?.RaiseCanExecuteChanged();
}
}
In the XAML I've simplified the binding of the ComboBox.SelectedItem
property like this:
<ComboBox ... SelectedItem="{Binding SelectedPort}"/>
CodePudding user response:
Your code has two issues:
- The most critical one, which actually prevents your code from executing properly, is the way you raise the
INotifyPropertyChanged.PropertyChanged
event. Instead of passing in the property name, you currently pass in the name of the backing field.
Instead of
OnPropertyChanged(nameof(portSelected));
it must be
OnPropertyChanged(nameof(SelectedPort));
To simplify the event invocation your invocator should use the CallerMemberName
attribute:
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
=> this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName);
Then call the method OnPropertyChanged
without any parameter, as the property name is now automatically detected:
OnPropertyChanged();
- The method
RaiseExecuteChangedCommand
is redundant. YourICommand
implementation of theCanExecuteChanged
event delegates event handlers to theCommandManager.RequerySuggested
event (which is correct). TheCommandManager.RequerySuggested
event is raised automatically by the WPF framework and because of the event delegation the framework will also raiseICommand.CanExecuteChanged
(because you attach allICommand.CanExecuteChanged
handlers to theCommandManager.RequerySuggested
event usingCommandManager.RequerySuggested = value
). If you don't want the framework to raise the CanExecuteChanged event for you you must remove theCommandManager.RequerySuggested = value
part i.e. don't attach client handlers to theCommandManager.RequerySuggested
event. Because now you will raise it explcitly from yourRaiseExecuteChangedCommand
method. So you can't have both. Either raiseICommand.CanExecuteChanged
explicitly or let theCommandManager
do it for you.