Alpha
Alpha

Reputation: 7858

WPF MVVM: Isolating command logic from ViewModel

I'm giving my first steps with WPF and MVVM, using Prism. So far, so good, but there's a certain design approach I cannot seem to achieve.

In my UI, I have two ways of opening a file. There's a browse button that can be clicked and an Open File dialog will appear, prompt for the file, and open it. You can also drag and drop a file on top the UI, and it'll open it.

In order to isolate the Browse logic, I created a command for it. The first code smell appears, I need a result from it that is not exposed by the ICommand interface.

public class BrowseFileCommand: ICommand
{
    public string ExecutionResult { get; private set; }

    public bool CanExecute(object parameter) => true;

    public void Execute(object parameter)
    {
        var openFileDialog = new OpenFileDialog()
        {
            Multiselect = false,
            Filter = "Event log files (*.evtx)|*.evtx"
        };

        ExecutionResult = openFileDialog.ShowDialog() == true ? openFileDialog.FileName : null;
    }

    public event EventHandler CanExecuteChanged;
}

Then, in my ViewModel class, I can invoke it like this:

public class MainWindowViewModel: BindableBase
{
    public DelegateCommand BrowseFileCommand { get; set; }

    public MainWindowViewModel()
    {
        BrowseFileCommand = new DelegateCommand(BrowseAndOpenFile, () => _browseFileCommand.CanExecute(null));
        // ...
    }

    private BrowseFileCommand _browseFileCommand = new BrowseFileCommand();

    private void BrowseAndOpenFile()
    {
        _browseFileCommand.Execute(null);
        var fileName = _browseFileCommand.ExecutionResult;
        if (!string.IsNullOrWhiteSpace(fileName))
            OpenFile(fileName);
    }

    // ...
}

Here are a few other code smells:

Is there a good way for me to completely isolate the logic to a Command / class?

Note the following design restrictions:

Upvotes: 0

Views: 753

Answers (1)

Euphoric
Euphoric

Reputation: 12849

ICommand is primarily intended to be used only for binding to UI. It is not meant to be used for separation of behavior and called from within normal code. Which is why you are getting your code smell.

Instead, use normal language features(methods, inheritance, composition, etc..) to separate the concerns and just use ICommand commands to expose specific operations to the UI for binding.

Upvotes: 0

Related Questions