Search code examples
c#wpficommand

Use ICommand in WPF


What is the best way to use commands in WPF ?

I use some commands, thoses commands can take a time to execute. I want that my application not freeze while running but I want the features to be disabled.

there is my MainWindow.xaml :

<Window ...>
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>          
        <Button Grid.Row="0"
                Grid.Column="0"
                Style="{StaticResource StyleButton}"
                Content="Load"
                Command="{Binding LoadCommand}"/>
        <Button Grid.Row="0"
                Grid.Column="1"
                Style="{StaticResource StyleButton}"
                Content="Generate"
                Command="{Binding GenerateCommand}"/>
    </Grid>
</Window>

and my MainViewModel.cs :

public class MainViewModel : ViewModelBase
{

    #region GenerateCommand
    #endregion

    #region Load command
    private ICommand _loadCommand;
    public ICommand LoadCommand
    {
        get
        {
            if (_loadCommand == null)
                _loadCommand = new RelayCommand(OnLoad, CanLoad);
            return _loadCommand;
        }
    }

    private void OnLoad()
    {
        //My code
    }
    private bool CanLoad()
    {
        return true;
    }
    #endregion
}

I saw a solution with background worker but I don't know how to use it. And I wonder if I should create one instance by command.

Is there a cleaner/best way ?


Solution

  • I want that my application not freeze while running but I want the features to be disabled.

    The key to prevent the application from freezing is to perform any long-running operation on a background thread. The easiest way to do this is to start a Task. To disable the window you could bind its IsEnabled property to a source property of the view model that you set prior to starting the task. The following sample code should give you the idea:

    public class MainViewModel : ViewModelBase
    {
        private RelayCommand _loadCommand;
        public ICommand LoadCommand
        {
            get
            {
                if (_loadCommand == null)
                    _loadCommand = new RelayCommand(OnLoad, CanLoad);
                return _loadCommand;
            }
        }
    
        private void OnLoad()
        {
            IsEnabled = false;
            _canLoad = false;
            _loadCommand.RaiseCanExecuteChanged();
    
            Task.Factory.StartNew(()=> { System.Threading.Thread.Sleep(5000); })  //simulate som long-running operation that runs on a background thread...
                .ContinueWith(task =>
                {
                    //reset the properties back on the UI thread once the task has finished
                    IsEnabled = true;
                    _canLoad = true;
                }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
        }
    
        private bool _canLoad = true;
        private bool CanLoad()
        {
            return _canLoad;
        }
    
        private bool _isEnabled;
        public bool IsEnabled
        {
            get { return _isEnabled; }
            set { _isEnabled = value; RaisePropertyChanged(); }
        }
    }
    

    Note that you cannot access any UI element from a background thread since controls have thread affinity: http://volatileread.com/Thread/Index?id=1056