Well not exactly an answer to my question, but here is the result of doing this dialog, complete with code so you can use it if you wish - free as in free speech and beer:
XAML Usage in another view (here CustomerView):
<UserControl
x:Class="DemoApp.View.CustomerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:DemoApp.View"
>
<Grid>
<Grid Margin="4" x:Name="ModalDialogParent">
<put all view content here/>
</Grid>
<controls:ModalDialog DataContext="{Binding Dialog}" OverlayOn="{Binding ElementName=ModalDialogParent, Mode=OneWay}" IsShown="{Binding Path=DialogShown}"/>
</Grid>
</UserControl>
Triggering from parent ViewModel (here CustomerViewModel):
public ModalDialogViewModel Dialog // dialog view binds to this
{
get
{
return _dialog;
}
set
{
_dialog = value;
base.OnPropertyChanged("Dialog");
}
}
public void AskSave()
{
Action OkCallback = () =>
{
if (Dialog != null) Dialog.Hide();
Save();
};
if (Email.Length < 10)
{
Dialog = new ModalDialogViewModel("This email seems a bit too short, are you sure you want to continue saving?",
ModalDialogViewModel.DialogButtons.Ok,
ModalDialogViewModel.CreateCommands(new Action[] { OkCallback }));
Dialog.Show();
return;
}
if (LastName.Length < 2)
{
Dialog = new ModalDialogViewModel("The Lastname seems short. Are you sure that you want to save this Customer?",
ModalDialogViewModel.CreateButtons(ModalDialogViewModel.DialogMode.TwoButton,
new string[] {"Of Course!", "NoWay!"},
OkCallback,
() => Dialog.Hide()));
Dialog.Show();
return;
}
Save(); // if we got here we can save directly
}
Here is the code:
ModalDialogView XAML:
<UserControl x:Class="DemoApp.View.ModalDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<UserControl.Resources>
<ResourceDictionary Source="../MainWindowResources.xaml" />
</UserControl.Resources>
<Grid>
<Border Background="#90000000" Visibility="{Binding Visibility}">
<Border BorderBrush="Black" BorderThickness="1" Background="AliceBlue"
CornerRadius="10,0,10,0" VerticalAlignment="Center"
HorizontalAlignment="Center">
<Border.BitmapEffect>
<DropShadowBitmapEffect Color="Black" Opacity="0.5" Direction="270" ShadowDepth="0.7" />
</Border.BitmapEffect>
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Style="{StaticResource ModalDialogHeader}" Text="{Binding DialogHeader}" Grid.Row="0"/>
<TextBlock Text="{Binding DialogMessage}" Grid.Row="1" TextWrapping="Wrap" Margin="5" />
<StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Grid.Row="2">
<ContentControl HorizontalAlignment="Stretch"
DataContext="{Binding Commands}"
Content="{Binding}"
ContentTemplate="{StaticResource ButtonCommandsTemplate}"
/>
</StackPanel>
</Grid>
</Border>
</Border>
</Grid>
</UserControl>
ModalDialogView code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DemoApp.View
{
/// <summary>
/// Interaction logic for ModalDialog.xaml
/// </summary>
public partial class ModalDialog : UserControl
{
public ModalDialog()
{
InitializeComponent();
Visibility = Visibility.Hidden;
}
private bool _parentWasEnabled = true;
public bool IsShown
{
get { return (bool)GetValue(IsShownProperty); }
set { SetValue(IsShownProperty, value); }
}
// Using a DependencyProperty as the backing store for IsShown. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsShownProperty =
DependencyProperty.Register("IsShown", typeof(bool), typeof(ModalDialog), new UIPropertyMetadata(false, IsShownChangedCallback));
public static void IsShownChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
ModalDialog dlg = (ModalDialog)d;
dlg.Show();
}
else
{
ModalDialog dlg = (ModalDialog)d;
dlg.Hide();
}
}
#region OverlayOn
public UIElement OverlayOn
{
get { return (UIElement)GetValue(OverlayOnProperty); }
set { SetValue(OverlayOnProperty, value); }
}
// Using a DependencyProperty as the backing store for Parent. This enables animation, styling, binding, etc...
public static readonly DependencyProperty OverlayOnProperty =
DependencyProperty.Register("OverlayOn", typeof(UIElement), typeof(ModalDialog), new UIPropertyMetadata(null));
#endregion
public void Show()
{
// Force recalculate binding since Show can be called before binding are calculated
BindingExpression expressionOverlayParent = this.GetBindingExpression(OverlayOnProperty);
if (expressionOverlayParent != null)
{
expressionOverlayParent.UpdateTarget();
}
if (OverlayOn == null)
{
throw new InvalidOperationException("Required properties are not bound to the model.");
}
Visibility = System.Windows.Visibility.Visible;
_parentWasEnabled = OverlayOn.IsEnabled;
OverlayOn.IsEnabled = false;
}
private void Hide()
{
Visibility = Visibility.Hidden;
OverlayOn.IsEnabled = _parentWasEnabled;
}
}
}
ModalDialogViewModel:
using System;
using System.Windows.Input;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Windows;
using System.Linq;
namespace DemoApp.ViewModel
{
/// <summary>
/// Represents an actionable item displayed by a View (DialogView).
/// </summary>
public class ModalDialogViewModel : ViewModelBase
{
#region Nested types
/// <summary>
/// Nested enum symbolizing the types of default buttons used in the dialog -> you can localize those with Localize(DialogMode, string[])
/// </summary>
public enum DialogMode
{
/// <summary>
/// Single button in the View (default: OK)
/// </summary>
OneButton = 1,
/// <summary>
/// Two buttons in the View (default: YesNo)
/// </summary>
TwoButton,
/// <summary>
/// Three buttons in the View (default: AbortRetryIgnore)
/// </summary>
TreeButton,
/// <summary>
/// Four buttons in the View (no default translations, use Translate)
/// </summary>
FourButton,
/// <summary>
/// Five buttons in the View (no default translations, use Translate)
/// </summary>
FiveButton
}
/// <summary>
/// Provides some default button combinations
/// </summary>
public enum DialogButtons
{
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration Ok
/// </summary>
Ok,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration OkCancel
/// </summary>
OkCancel,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration YesNo
/// </summary>
YesNo,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration YesNoCancel
/// </summary>
YesNoCancel,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration AbortRetryIgnore
/// </summary>
AbortRetryIgnore,
/// <summary>
/// As System.Window.Forms.MessageBoxButtons Enumeration RetryCancel
/// </summary>
RetryCancel
}
#endregion
#region Members
private static Dictionary<DialogMode, string[]> _translations = null;
private bool _dialogShown;
private ReadOnlyCollection<CommandViewModel> _commands;
private string _dialogMessage;
private string _dialogHeader;
#endregion
#region Class static methods and constructor
/// <summary>
/// Creates a dictionary symbolizing buttons for given dialog mode and buttons names with actions to berform on each
/// </summary>
/// <param name="mode">Mode that tells how many buttons are in the dialog</param>
/// <param name="names">Names of buttons in sequential order</param>
/// <param name="callbacks">Callbacks for given buttons</param>
/// <returns></returns>
public static Dictionary<string, Action> CreateButtons(DialogMode mode, string[] names, params Action[] callbacks)
{
int modeNumButtons = (int)mode;
if (names.Length != modeNumButtons)
throw new ArgumentException("The selected mode needs a different number of button names", "names");
if (callbacks.Len