0
votes

I am creating an WPF following MVVM. In this I have a button that I would like to Open a FolderBrowserDialog so a user can select a folder path. I know opening dialog is something widely discussed in terms of MVVM because .ShowDialog from the view Model is anti-pattern. So after some research I found this post and answer How to use a FolderBrowserDialog from a WPF application with MVVM where the accepted answer suggests doing:

var dlg = new FolderBrowserDialog();
DialogResult result = dlg.ShowDialog();

Does this break MVVM? If so whats an alternative that doesn't rely on frameworks such as Prism?

2

2 Answers

0
votes

With MVVM, what's considered a pattern vs. an anti-pattern is highly subjective and often equates to religious dogma.

MVVM is a great pattern because it helps you separate concerns, allows you to more easily test your code, and opens up the possibility of easily changing your UI in the future.

How you handle the FolderBrowserDialog and other things similar to it, depend on your app and your particular needs.

If your app is small, or you're only calling the dialog once, or you're not unit testing your view-model, then just call the FolderBrowserDialog from your view-model and be done with it.

However, if strict separation of concerns is important to you and your app, then consider creating a service class to handle folder dialog work that you can call from your app's view-model.

Service Class

This is a simple public class that gives your app's view-models an API to open a FolderBrowserDialog. Here is a super-simple example:

public class FolderBrowserDialogService
{
    public FolderBrowserResponse ShowFolderBrowserDialog()
    {
        var dialog = new FolderBrowserDialog();
        var result = dialog.ShowDialog();
        
        // TODO: Convert result to FolderBrowserResponse
        // FolderBrowserResponse is a custom type you create so your view-model
        // doesn't know about DialogResult

        return folderBrowserResponse;
    }
}

Then, in your app's view-model, you can call the service class like so:

var service = new FolderBrowserDialogService();
var result = service.ShowFolderBrowserDialog();

//TODO: Process the result...

I hope this gives you some ideas.

0
votes

I came up with a hack to get a folder browser in WPF, since Microsoft decided it was no worth adding it to WPF. No third party libraries are involved in this one. I also found a work around to use it for MVVM situations.

To get the (hacked) folder browser, I employed the SaveFileDialog

  • I added the following imports

    using System.IO;
    using Microsoft.Win32;
    
  • Then I created a method to Get Folder Path

      public string GetFolderPath()
     {
         SaveFileDialog folderDialog = new SaveFileDialog
         {
             AddExtension = false,
             Title = "Select a Directory",
             Filter = "All|*.*",
             InitialDirectory = Environment.GetFolderPath(
             Environment.SpecialFolder.MyDocuments),
             CheckFileExists = false,
             CheckPathExists = true,
             FileName = Path.GetFileName("Select a folder"),
         };
    
         // Return null if the user does not click the OK button of the dialog displayed.
         if (folderDialog.ShowDialog() != true)
         {
             return null;
         }
    
         // User clicked OK, get the full path of the predefined file name provided.
         string path = folderDialog.FileName;
    
         // Get the parent directory of the dummy/predefined file name supplied.
         path = Directory.GetParent(path).FullName;
    
         // If user changes or delete the directory, re-create it.
         if (!Directory.Exists(path))
         {
             Directory.CreateDirectory(path);
         }
    
         // return the folder path.
         return path;
     }
    

It worked pretty well for my simple projects. But my newest project followed strict MVVM pattern so View, Model, and View Model were grouped into separate projects:

  • MyApp.View

  • MyApp.Core

  • MyApp.ViewModel

Each Core and ViewModel projects were both using the .NET Standard Library, which does not have the Microsoft.Win32 library. So I had to find a way to get file browser and the (hacked) folder browser into the view model project.

In order to use the FileDialog and the hacked FolderBroser, I

  1. Created an Interface in the Core project which was a shared library in both the View and ViewModel projects

    /// <summary>
    /// Interface for handling file dialog results
    /// </summary>
    public interface IDialogResults
    {
        /// <summary>
        /// Opens a file dialog for user to select files
        /// </summary>
        /// <returns></returns>
       string GetFilePath();
    
        /// <summary>
        /// Opens a folder dialog box for user to select folders
        /// </summary>
        /// <returns></returns>
        string GetFolderPath();
    }
    
  2. Then inherited it in the user control's partial class

public partial class FolderDialogUserControl : IDialogResults which implemented the methods of the interface.

        #region Implementation of IDialogResults

    /// <inheritdoc />
    public string GetFilePath()
    {
        OpenFileDialog fileDialog = new OpenFileDialog()
        {
            AddExtension = true,
            CheckFileExists = true,
            CheckPathExists = true,
            Filter = "All|*.*",
            InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
        };

        if (!fileDialog.ShowDialog().HasValue)
        {
            return null;
        }

        // return the full path of the file selected.
        return fileDialog.FileName;
    }

    /// <inheritdoc />
    public string GetFolderPath()
    {
        SaveFileDialog folderDialog = new SaveFileDialog
        {
            AddExtension = false,
            Title = "Select a Directory",
            Filter = "Database File|*.ldf;*.mdf", // Prevents display of other file types
            InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
            CheckFileExists = false,
            CheckPathExists = true,
        };

        // Return null if the user does not click the OK button of the dialog displayed.
        if (folderDialog.ShowDialog() != true)
        {
            return null;
        }

        // User clicked OK, get the full path of the predefined file name provided.
        string path = folderDialog.FileName;

        // Get the parent directory of the dummy/predefined file name supplied.
        path = Directory.GetParent(path).FullName;

        // If user changes or delete the directory, re-create it.
        if (!Directory.Exists(path))
        {
            Directory.CreateDirectory(path);
        }

        // return the folder path.
        return path;
    }

    #endregion
  1. Then in the DialogUserControl.xaml file I created the button to initiate the folder open command. In the button properties,I added the command binding to the ICommand Property from the DialogUserControlViewModel; then I added the DialogUserControl itself as CommandParameter

     Command="{Binding OpenFolderDialog}"
     CommandParameter="{Binding ElementName=DialogUserControl}"
    
  2. Then in the DialogUser ViewModel, I added the following code

        /// <summary>
    /// Opens a folder dialog for user to select a destination
    /// </summary>
    /// <param name="parameter">FolderDialogUserControl.xaml.cs</param>
    private void BrowseFolderDialog(object parameter)
    {
        BackupDestination = (parameter as IDialogResults)?.GetFolderPath();
    }
    
    /// <summary>
    /// Opens a File Dialog for user to select the file
    /// </summary>
    /// <param name="parameter">FolderDialogUserControl.xaml.cs file</param>
    private void BrowseFileDialog(object parameter)
    {
        RestoreSource = (parameter as IDialogResults)?.GetFilePath();
    }
    

Aside normal folder browser feel tradeoff (which was hardly noticeable by users), everything worked fine.