1

I have a search dialog where I want to disable the search button during the search. This is the current code but the button does not get deactivated

View:

<Button Content="Search" 
        Command="{Binding StartSearchCommand}" 
        IsEnabled="{Binding IsNotSearching}" />

ViewModel:

private bool _isNotSearching;
public bool IsNotSearching
{
    get { return _isNotSearching; }
    set
    {
        _isNotSearching = value;
        OnPropertyChanged("IsNotSearching");
    }
}


private RelayCommand<object> _startSearchCommand;
public ICommand StartSearchCommand
{
    get
    {
        if (_startSearchCommand == null)
            _startSearchCommand = new RelayCommand<object>(p => ExecuteSearch());
        return _startSearchCommand;
    }
}
private void ExecuteSearch()
{
    IsNotSearching = false;
    //do some searching here
    IsNotSearching = true;
}
juergen d
  • 201,996
  • 37
  • 293
  • 362
  • 1
    That's because you block UI thread by perfoming Thread.Sleep. If you would perform some task in background thread everything should work as you expect – netaholic Sep 09 '15 at 13:28
  • Tried it. Gave it something to do. But the button looks clicked in the time of execution and not deactivated – juergen d Sep 09 '15 at 13:35
  • It is happening but so fast you don't see it. ExecuteSearch() does not update the UI until it is done so IsNotSearching = false and IsNotSearching = true happend at the same time. – paparazzo Sep 09 '15 at 13:52
  • @Frisbee: I calculate something that takes very long. I can see it is clicked unitl it finishes – juergen d Sep 09 '15 at 14:08
  • @juergend So you can see it clicked. What does that prove? – paparazzo Sep 09 '15 at 14:12
  • Create a boolean variable and set it to false and raisecanexecute of that command, should do it. Connect the bool to isenabled property of the button. Also enable twoway mode binding – Gilad Sep 09 '15 at 14:17
  • @Frisbee: It stays clicked until the search is complete. – juergen d Sep 09 '15 at 14:18
  • So what does that prove? That is XAML dealing with the button. – paparazzo Sep 09 '15 at 14:19
  • @Frisbee: Neverrmind. Thanks for the assistance. – juergen d Sep 09 '15 at 14:20

4 Answers4

9

I've made an AsyncDelegateCommand for that reason (based on famous DelegateCommand), it internally disable command (in UI) during executing command action:

public class AsyncDelegateCommand : ICommand
{
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;
    bool _running;

    public event EventHandler CanExecuteChanged;

    public AsyncDelegateCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return (_canExecute == null ? true : _canExecute(parameter)) && !_running;
    }

    public async void Execute(object parameter)
    {
        _running = true;
        Update();
        await Task.Run(() => _execute(parameter));
        _running = false;
        Update();
    }

    public void Update()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    }
}

xaml:

<Button Command="{Binding SomeCommand}" .../>

ViewModel:

AsyncDelegateCommand SomeCommand { get; }

    // in constructor
    SomeCommand = new AsyncDelegateCommand(o =>  { Thread.Sleep(5000); }); // code to run
Sinatr
  • 20,892
  • 15
  • 90
  • 319
0

I am not sure about looks when it was clicked and disabled - it is possible that it looks same.

But netaholic is right. If you execute logic in main UI thread it is possible that UI is frozen till your execution end (and isn't changed or changed to fast).

Try to put your logic in dispatcher. Like in next part of the code.

Application.Current.Dispatcher.BeginInvoke(
  DispatcherPriority.Background,
  new Action(() => { 
    //do some searching here
  }));

BTW, I took this code there.

EDIT: don't use this for your solution. This is wrong (check comments).

Use dispatcher when you need to update visual element async.

Application.Current.Dispatcher.BeginInvoke(
      DispatcherPriority.Background,
      new Action(() => { 
        textbox.Content = "some result";
      }));
Community
  • 1
  • 1
RredCat
  • 5,259
  • 5
  • 60
  • 100
0

You could try command can execute to disable the command without adding IsEnabled property

private RelayCommand _startSearchCommand;
    public RelayCommand StartSearchCommand
    {
        get
        {
            return _startSearchCommand??
                   (_startSearchCommand=
                //Added can execute command , only execute if system is not busy at the moment.
                       new RelayCommand(async () => await OnExecuteSearch(), () => !IsBusy));
        }

    }

Command handler

internal async Task OnExecuteSearch()
    {
        IsBusy = true;

        //Execute your action
        //and finally
        IsBusy = false;

    }

Xaml

 <Button Command="{Binding StartSearchCommand}">

The canexecute in the Relay Command will control the IsEnabled property for you.

Eldho
  • 7,795
  • 5
  • 40
  • 77
0

This code must work.

private async void ExecuteSearch() {
        IsNotSearching = false;

        var task = Task.Factory.StartNew(() => {
            //do some searching here
        });
        await task;

        IsNotSearching = true;
    }
Lance
  • 764
  • 1
  • 7
  • 18