C
C#6mo ago
Eple

✅ Help needed: Buttons update enabled state after two clicks.

Hello, I need help to find out why it takes two clicks before the buttons change the enabled state. Please see the attached video and also my code below:
<StackLayout>
<Button Text="Start the service" Command="{Binding StartTheServiceCommand}" />
</StackLayout>

<StackLayout>
<Button Text="Stop the service" Command="{Binding StopTheServiceCommand}" />
</StackLayout>
<StackLayout>
<Button Text="Start the service" Command="{Binding StartTheServiceCommand}" />
</StackLayout>

<StackLayout>
<Button Text="Stop the service" Command="{Binding StopTheServiceCommand}" />
</StackLayout>
public MainPageViewModel(ILogger<MainPageViewModel> logger, WorkerService workerService)
{
_logger = logger;
_workerService = workerService;

StartTheServiceCommand = new Command(
execute: () =>
{
_logger.LogInformation("Starting the service at: {time}", DateTimeOffset.Now);
Task.Run(async () => await _workerService.StartAsync(new()));
RefreshCanExecutes();
},
canExecute: () => !_workerService.IsRunning);

StopTheServiceCommand = new Command(
execute: () =>
{
_logger.LogInformation("Stopping the service at: {time}", DateTimeOffset.Now);
Task.Run(async () => await _workerService.StopAsync(new()));
RefreshCanExecutes();
},
canExecute: () => _workerService.IsRunning);
}

public ICommand StartTheServiceCommand { get; }
public ICommand StopTheServiceCommand { get; }

private void RefreshCanExecutes()
{
(StartTheServiceCommand as Command)!.ChangeCanExecute();
(StopTheServiceCommand as Command)!.ChangeCanExecute();
}
public MainPageViewModel(ILogger<MainPageViewModel> logger, WorkerService workerService)
{
_logger = logger;
_workerService = workerService;

StartTheServiceCommand = new Command(
execute: () =>
{
_logger.LogInformation("Starting the service at: {time}", DateTimeOffset.Now);
Task.Run(async () => await _workerService.StartAsync(new()));
RefreshCanExecutes();
},
canExecute: () => !_workerService.IsRunning);

StopTheServiceCommand = new Command(
execute: () =>
{
_logger.LogInformation("Stopping the service at: {time}", DateTimeOffset.Now);
Task.Run(async () => await _workerService.StopAsync(new()));
RefreshCanExecutes();
},
canExecute: () => _workerService.IsRunning);
}

public ICommand StartTheServiceCommand { get; }
public ICommand StopTheServiceCommand { get; }

private void RefreshCanExecutes()
{
(StartTheServiceCommand as Command)!.ChangeCanExecute();
(StopTheServiceCommand as Command)!.ChangeCanExecute();
}
4 Replies
canton7
canton76mo ago
RefreshCanExecutes(); doesn't wait for the service to actually start. You queue a bit of work onto the thread pool to start the worker at some point in the future, then immediately call RefreshCanExecutes();, which calls _workerService.IsRunning. But the worker isn't running yet You shouldn't be discarding tasks. The fact that you had to throw a Task.Run into the mix is presumably to work around the fact that the compiler was moaning at you for now awaiting the _workerService.StartAsync call? That was your clue 😉
public MainPageViewModel(ILogger<MainPageViewModel> logger, WorkerService workerService)
{
_logger = logger;
_workerService = workerService;

StartTheServiceCommand = new Command(
execute: async () =>
{
_logger.LogInformation("Starting the service at: {time}", DateTimeOffset.Now);
await _workerService.StartAsync(new());
RefreshCanExecutes();
},
canExecute: () => !_workerService.IsRunning);

StopTheServiceCommand = new Command(
execute: async () =>
{
_logger.LogInformation("Stopping the service at: {time}", DateTimeOffset.Now);
await _workerService.StopAsync(new());
RefreshCanExecutes();
},
canExecute: () => _workerService.IsRunning);
}

public ICommand StartTheServiceCommand { get; }
public ICommand StopTheServiceCommand { get; }

private void RefreshCanExecutes()
{
(StartTheServiceCommand as Command)!.ChangeCanExecute();
(StopTheServiceCommand as Command)!.ChangeCanExecute();
}
public MainPageViewModel(ILogger<MainPageViewModel> logger, WorkerService workerService)
{
_logger = logger;
_workerService = workerService;

StartTheServiceCommand = new Command(
execute: async () =>
{
_logger.LogInformation("Starting the service at: {time}", DateTimeOffset.Now);
await _workerService.StartAsync(new());
RefreshCanExecutes();
},
canExecute: () => !_workerService.IsRunning);

StopTheServiceCommand = new Command(
execute: async () =>
{
_logger.LogInformation("Stopping the service at: {time}", DateTimeOffset.Now);
await _workerService.StopAsync(new());
RefreshCanExecutes();
},
canExecute: () => _workerService.IsRunning);
}

public ICommand StartTheServiceCommand { get; }
public ICommand StopTheServiceCommand { get; }

private void RefreshCanExecutes()
{
(StartTheServiceCommand as Command)!.ChangeCanExecute();
(StopTheServiceCommand as Command)!.ChangeCanExecute();
}
Also, if you use as, always check for null. If you don't expect the cast to fail, use a normal cast, not as. The reason is that if you use as but the cast does fail unexpectedly, you'll get an unhelpful NullReferenceException, rather than a helpful InvalidCastException. So:
private void RefreshCanExecutes()
{
((Command)StartTheServiceCommand).ChangeCanExecute();
((Command)StopTheServiceCommand).ChangeCanExecute();
}
private void RefreshCanExecutes()
{
((Command)StartTheServiceCommand).ChangeCanExecute();
((Command)StopTheServiceCommand).ChangeCanExecute();
}
Eple
EpleOP6mo ago
@canton7 thank you for your responses, they helped.
canton7
canton76mo ago
Great! Has that solved your problem?
Eple
EpleOP6mo ago
Yes it has.
Want results from more Discord servers?
Add your server