Reputation: 690
I tried to find answers to my question on the internet, but no answer satisfied me enough. I am writing WPF application and i'm trying to implement dialog mechanism. I have a simple ViewModel, and when some event happens, i would like to show a dialog, collect some output data from it and store it in "parent" View Model. My method in a view model looks like this:
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
}
}
Implementation of ShowDialog method:
public void ShowDialog(DialogVM dialogVM)
{
var dialog = new DialogBox();
var mainWindow = Application.Current.MainWindow as MainWindow;
dialog.DataContext = dialogVM;
dialog.Owner = mainWindow;
dialog.Show();
}
Now, let's imagine that i need some data from the dialog. How can i pass it to my ViewModel in a proper way?
Upvotes: 0
Views: 1791
Reputation: 28968
View model shouldn't handle view elements. A dialog is a view element.
The view model can trigger user input by raising and event e.g., an error event with an data model as event args. The view that has registered to the event shows a dialog to collect user input and stores them in the previously received data model. The view then executes a command on the view model to pass back the data model.
Instead of an event you can also bind the view to a property of the view model e.g. of type bool
. On property change show the dialog and return the result using a ICommand
.
Alternatively let the view model expose a flag e.g. HasException
and a property ExceptionDialogModel
which can be used to bind a custom dialog or form. Then create a simple modal dialog yourself:
ExampleDialog
<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>
You can put the Grid
anywhere in your Window
and toggle the Visibility
. It will overlay the parent Window
and has modal behavior.
Bind the DataContext
to the ExceptionDialogModel
so that the data is send back via TwoWay
binding. Use a command to trigger a retry procedure (e.g., an OK or Retry button).
The Visibility
can bind to the HasException
property. You can animate this dialog and give it any look and feel you like.
Upvotes: 1
Reputation: 97
I believe you are doing this backwards. You should pass reference to your view model to the dialog, not the other way around because view model should be separate and not aware of the view's mechanics. Dialog, on the other hand, knows which properties of view model it needs to set. So it will be something like the following:
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()
}
}
Upvotes: 0