如何在WPF MVVM中正确地从对话框中获取输出



我试图在互联网上找到问题的答案,但没有足够的答案让我满意。我正在编写WPF应用程序,并试图实现对话机制。我有一个简单的ViewModel,当某个事件发生时,我想显示一个对话框,从中收集一些输出数据,并将其存储在"父"视图模型中。我在视图模型中的方法如下:

private void Expand()
{
...
catch(ArgumentNullException)
{
Shrink();
var errorDialogVM = new DialogVM(new Dialog() { Type = DialogType.Error, Message = $"Unauthorized access to "{FileManager.GetDirectoryName(Path)}" directory!" });
DialogService.ShowDialog(errorDialogVM);
//Here i need output from dialog
}
}

ShowDialog方法的实现:

public void ShowDialog(DialogVM dialogVM)
{
var dialog = new DialogBox();
var mainWindow = Application.Current.MainWindow as MainWindow;
dialog.DataContext = dialogVM;
dialog.Owner = mainWindow;
dialog.Show();
}

现在,让我们想象一下,我需要对话框中的一些数据。如何以正确的方式将其传递给ViewModel?

视图模型不应该处理视图元素。对话框是一个视图元素
视图模型可以通过引发和事件触发用户输入,例如,将数据模型作为事件参数的错误事件。已注册到事件的视图显示一个对话框,用于收集用户输入并将其存储在以前接收的数据模型中。然后,视图对视图模型执行一个命令,以将数据模型传递回。

您也可以将视图绑定到视图模型的属性,例如类型为bool的属性,而不是事件。属性更改时,显示对话框并使用ICommand返回结果。

或者,让视图模型公开一个标志,例如HasException和一个属性ExceptionDialogModel,可以用来绑定自定义对话框或表单。然后自己创建一个简单的模态对话框:

示例对话框

<Grid x:Name="ExampleDialog"
Visibility="Visible"
Panel.ZIndex="100"
VerticalAlignment="Top">
<Rectangle Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=ActualWidth}"
Height="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=ActualHeight}"
Fill="Gray"
Opacity="0.7" />
<Grid Width="400"
Height="200">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Border Grid.RowSpan="2"
Background="LightGray"
BorderBrush="Black"
BorderThickness="1">
<Border.Effect>
<DropShadowEffect BlurRadius="5"
Color="Black"
Opacity="0.6" />
</Border.Effect>
</Border>
<TextBlock Grid.Row="0"
TextWrapping="Wrap"
Margin="30"
Text="I am a modal dialog and my Visibility or Opacity property can be easily modified by a trigger or a nice animation" />
<StackPanel Orientation="Horizontal"
Grid.Row="1"
HorizontalAlignment="Right"
Height="50">
<Button x:Name="OkButton"
Content="Ok"
Width="80" />
<Button x:Name="CancelButton"
Margin="30,0,30,0"
Content="Cancel"
Width="80" />
</StackPanel>
</Grid>
<Grid.Triggers>
<EventTrigger RoutedEvent="Button.Click">
<BeginStoryboard>
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="ExampleDialog"
Storyboard.TargetProperty="Visibility"
Duration="0">
<DiscreteObjectKeyFrame Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Grid.Triggers>
</Grid>

您可以将Grid放在Window中的任何位置,并切换Visibility。它将覆盖父Window并具有模态行为
DataContext绑定到ExceptionDialogModel,以便通过TwoWay绑定发送回数据。使用命令触发重试过程(例如,确定重试按钮(
Visibility可以绑定到HasException属性。您可以设置此对话框的动画,并赋予它任何您喜欢的外观和感觉。

我相信你是在倒退。您应该将对视图模型的引用传递给对话框,而不是相反,因为视图模型应该是独立的,并且不知道视图的机制。另一方面,Dialog知道需要设置视图模型的哪些属性。因此,它将类似于以下内容:

public class MyDialog : Dialog
{
public MyDialog(DialogVM ViewModel) {
this.InitializeComponent();
this.DataContex = ViewModel;
// TODO: Bind to view model's properties in XAML or set them on OnClose()
}
}

最新更新