如何正确通知视图图层打开对话框



我有一个WPF应用程序,我正在努力遵守MVVM模式规则。我的一个视图包含按钮:

<Button
Command="{Binding BrowseCommand}"
Margin="50, 0, 0, 0"
Style="{StaticResource CommonButtonStyle}"
Width="100"
Height="30">
<TextBlock
Text="Browse"/>
</Button>

按钮命令调用一个方法:

private void Browse(object sender)
{
DialogService.BrowseForDestinationPath(DestinationPath);
}

此方法的主要目的是显示"选择目录对话框",收集数据并将其返回到视图模型。

public static class DialogService
{
public static event Action<string> FolderBrowseRequested;
...
public static void BrowseForDestinationPath(string initialPath)
{
FolderBrowseRequested?.Invoke(initialPath);
}
}

在我的DialogService类中定义的事件被调用,并且位于对话框代码后面的subscriber方法被触发:

protected void OnFolderBrowseRequested(string initialPath)
{
string destinationPath = initialPath;
var browsingDialog = new VistaFolderBrowserDialog();
if(browsingDialog.ShowDialog(this).GetValueOrDefault())
{
destinationPath = browsingDialog.SelectedPath;
var dataContext = DataContext as UnpackArchiveWindowViewModel;
if (dataContext != null)
dataContext.DestinationPath = destinationPath;
}
DialogService.FolderBrowseRequested -= OnFolderBrowseRequested; //so dumb
}

问题是我真的不喜欢这个解决方案,我确信它不必要地复杂和不雅。如何在点击按钮时正确显示对话框,收集一些数据并将其传递到我们的视图模型?我想保持视图和视图模型分离,并充分尊重MVVM制度。

您可以首先在界面中描述DialogService所需的行为。

public interface IDialogService
{
void BrowseForDestinationPath(string initialPath);
event PathSelectedEvent PathSelected;
}
public delegate void PathSelectedEvent(string destinationPath);  

您的ViewModel将包含IDialogService类型的成员,并订阅PathSelectedEvent。BrowseForDestinationPath方法将使用使用命令调用的Browse方法进行调用。

然后,您可以创建一个实现IDialogService的用户控件。您可以通过ViewModels构造函数注入它,或者如果您的ViewModel具有类似的属性

public IDialogService FolderBorwser {get;set;}

这种方法的好处是,视图模型所知道的只是一个接口。现在,您将创建具体实例的责任委派给其他人。我建议使用像Unity或MEF这样的注入容器,因为它们处理管理和解决依赖关系的工作。

我鼓励您编写自己的逻辑,因为它可以帮助您理解在MVVM中打开对话框的问题,但如果您遇到了困难或不想采取简单的方法,有一个名为MVVM对话框的库可以帮助您解决这些问题。

使用这个库,您可以像这样编写代码。

private void Browse(object sender)
{
var settings = new FolderBrowserDialogSettings
{
Description = "This is a description"
};
bool? success = dialogService.ShowFolderBrowserDialog(this, settings);
if (success == true)
{
// Do something with 'settings.SelectedPath'
}
}

最新更新