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
,我
在 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();
}
然后在用户控件的部分继承了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
然后在 DialogUserControl.xaml
文件中我创建了启动文件夹打开命令的按钮。在按钮属性中,我将命令绑定添加到 DialogUserControlViewModel 中的 ICommand 属性;然后我将 DialogUserControl 本身添加为 CommandParameter
Command="{Binding OpenFolderDialog}"
CommandParameter="{Binding ElementName=DialogUserControl}"
然后在 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();
}
除了正常的文件夹浏览器感觉权衡(用户几乎不会注意到)之外,一切正常。
我正在按照 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
,我
在 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(); }
然后在用户控件的部分继承了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
然后在
DialogUserControl.xaml
文件中我创建了启动文件夹打开命令的按钮。在按钮属性中,我将命令绑定添加到 DialogUserControlViewModel 中的 ICommand 属性;然后我将 DialogUserControl 本身添加为 CommandParameterCommand="{Binding OpenFolderDialog}" CommandParameter="{Binding ElementName=DialogUserControl}"
然后在 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(); }
除了正常的文件夹浏览器感觉权衡(用户几乎不会注意到)之外,一切正常。