BrowseFolderDialog - 这会破坏 MVVM 吗?

BrowseFolderDialog - Does this break MVVM?

我正在按照 MVVM 创建 WPF。在此我有一个按钮,我想打开一个 FolderBrowserDialog 以便用户可以 select 文件夹路径。我知道打开对话框在 MVVM 方面被广泛讨论,因为视图模型中的 .ShowDialog 是反模式的。因此,经过一些研究后,我发现这个 post 并回答 How to use a FolderBrowserDialog from a WPF application with MVVM 接受的答案建议这样做:

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

这会破坏 MVVM 吗?如果是这样,不依赖于 Prism 等框架的替代方案是什么?

对于 MVVM,模式与反模式的区别是非常主观的,通常等同于宗教教条。

MVVM 是一个很棒的模式,因为它可以帮助您分离关注点,让您更轻松地测试您的代码,并开启了在未来轻松更改您的 UI 的可能性。

您如何处理 FolderBrowserDialog 和其他类似的事情,取决于您的应用和您的特殊需求。

如果您的应用很小,或者您只调用一次对话框,或者您没有对您的视图模型进行单元测试,那么只需从您的视图模型中调用 FolderBrowserDialog 即可完成有了它。

但是,如果严格分离关注点对您和您的应用很重要,那么请考虑创建一个服务 class 来处理您可以从应用的视图模型调用的文件夹对话框工作。

服务Class

这是一个简单的 public class,它为您的应用的视图模型提供一个 API 来打开一个 FolderBrowserDialog。这是一个超级简单的例子:

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;
    }
}

然后,在您应用的视图模型中,您可以像这样调用服务 class:

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

//TODO: Process the result...

我希望这能给你一些想法。

我想出了一个在 WPF 中获取文件夹浏览器的技巧,因为 Microsoft 认为不值得将其添加到 WPF 中。没有第三方图书馆参与其中。 我还找到了将其用于 MVVM 情况的解决方法。

为了获取(被黑的)文件夹浏览器,我使用了 SaveFileDialog

  • 我添加了以下导入

    using System.IO;
    using Microsoft.Win32;
    
  • 然后我创建了一个获取文件夹路径的方法

      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;
     }
    

它对我的简单项目非常有效。但我的最新项目遵循严格的 MVVM 模式,因此视图、模型和视图模型被分组到单独的项目中:

  • MyApp.View

  • MyApp.Core

  • MyApp.View型号

每个 Core 和 ViewModel 项目都使用 .NET Standard 图书馆,没有 Microsoft.Win32 图书馆。所以我必须找到一种方法将文件浏览器和(被黑的)文件夹浏览器放入视图模型项目中。

为了使用FileDialog和被黑的FolderBroser,我

  1. 在 Core 项目中创建了一个接口,它是 View 和 ViewModel 项目中的共享库

    /// <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. 然后在用户控件的部分继承了class

public partial class FolderDialogUserControl : IDialogResults 它实现了接口的方法。

        #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. 然后在 DialogUserControl.xaml 文件中我创建了启动文件夹打开命令的按钮。在按钮属性中,我将命令绑定添加到 DialogUserControlViewModel 中的 ICommand 属性;然后我将 DialogUserControl 本身添加为 CommandParameter

     Command="{Binding OpenFolderDialog}"
     CommandParameter="{Binding ElementName=DialogUserControl}"
    
  2. 然后在 DialogUser ViewModel 中,我添加了以下代码

        /// <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();
    }
    

除了正常的文件夹浏览器感觉权衡(用户几乎不会注意到)之外,一切正常。