I did come across some examples of how to accomplish it but none were satisfactorily easy. I could probably have dissected them enough to figure it out, but creating interfaces or custom UserControls seemed overkill. I just want to use a MessageBox! But I want to do it from the View rather than the ViewModel in order to keep with the design pattern.
Here is a solution I didn’t see anywhere. I may have missed it, and in that case here is another example.
I used my base project code from HERE (The non-refactored original is HERE ). Special thanks to JR and Sean Feldman for the simple MVVM base code.
PROBLEM
The basic premise of MVVM is that the View controls all aspects of the presentation. This includes alerts or other various MessageBox-style popups. Somehow the ViewModel that controls business logic needs to inform the View that an alert should be popped up with certain data.
SOLUTION
To start, we create a class that will encapsulate all our Alert settings. This will include fields for Alert Message, Caption, type of button options, and finally an Action command to execute if the message response is affirmative.
public class AlertSettings { public enum AlertType { YesNo, OkCancel, OkOnly } public string DialogMessage { get; private set; } public string HeaderText { get; private set; } public AlertType Alert_Type { get; private set; } private Action commandAction; private ICommand command; public ICommand CommandToExecute { get { if (command == null) { command = new Command(commandAction); } return command; } private set { command = value; } } public AlertSettings(string dialogMessage, string headerText, AlertType alertType, Action delegateMethodToExecute) { this.DialogMessage = dialogMessage; this.HeaderText = headerText; this.Alert_Type = alertType; this.commandAction = delegateMethodToExecute; } }
Next we will wire up the View to hold a generic FrameworkElement for use as a listener object. This object will be notified of an incoming Alert request using its Tag to hold the Alert settings.
private FrameworkElement alertListener; public InvoiceView() { InitializeComponent(); alertListener = ConstructAlertListener(); this.AddLogicalChild(alertListener); } ///The ShowAlert() method that is called in the TargetUpdated event handler will execute the actual showing of the Alert message in whatever way the View is designed to do. In this case, I’m just using our handy MessageBox./// Creates the generic alert object with proper databinding on the Tag property. /// NotifyOnTargetUpdated is used to trigger the alert display /// ///private FrameworkElement ConstructAlertListener() { // This for binding to the ViewModel on an AlertSettings object using // INotifyPropertyChanged to trigger the update on the View Binding tagBinding = new Binding(); tagBinding.Path = new PropertyPath("AlertSettings"); tagBinding.Mode = BindingMode.OneWay; tagBinding.NotifyOnTargetUpdated = true; FrameworkElement element = new FrameworkElement(); element.Visibility = Visibility.Hidden; element.SetBinding(TagProperty, tagBinding); element.TargetUpdated += alertListener_TargetUpdated; return element; } /// /// Event handler for executing the alert message /// /// /// private void alertListener_TargetUpdated(object sender, DataTransferEventArgs e) { if (alertListener.Tag != null) { ShowAlert(); } }
///Last of all, we trigger the data in the ViewModel. This is simply done by creating an Alert settings property that will trigger the OnPropertyChanged action, then setting this property with a new instantiation of the settings class whenever an alert is desired.private void ShowAlert() { if (alertListener.Tag == null) { return; } if (alertListener.Tag is AlertSettings) { AlertSettings settings = (AlertSettings)alertListener.Tag; ShowAlertByAlertType(settings); } } /// /// Executes the alert using the settings saved in the alert object Tag property /// /// /// Shows an alert based on alert settings. This implementation uses MessageBox, but other /// implementations can use whatever is desired using the alert settings specified. /// AlertSettings object to be used to form an alert message private void ShowAlertByAlertType(AlertSettings settings) { switch (settings.Alert_Type) { case AlertSettings.AlertType.OkOnly: MessageBox.Show(settings.DialogMessage, settings.HeaderText, System.Windows.MessageBoxButton.OK); break; case AlertSettings.AlertType.OkCancel: if (MessageBox.Show(settings.DialogMessage, settings.HeaderText, MessageBoxButton.OKCancel) == MessageBoxResult.OK) { settings.CommandToExecute.Execute(null); } break; case AlertSettings.AlertType.YesNo: if (MessageBox.Show(settings.DialogMessage, settings.HeaderText, MessageBoxButton.YesNo) == MessageBoxResult.Yes) { settings.CommandToExecute.Execute(null); } break; default: break; } }
///The alert is triggered using this code:private AlertSettings alertSettings; public AlertSettings AlertSettings { get { return alertSettings; } set { alertSettings = value; OnPropertyChanged(PropertyOf /// This was created to set the alert data and then trigger to View using INotifyPropertyChanged /// .Resolve(x => x.AlertSettings)); } } private void VerifyInvoiceCreationAction() { DisplayMessage = string.Format("Thanks for creating invoice: {0} {1}", Invoice.Id, Invoice.Receiver); }
string alertMessage = string.Format("Are you sure you want to create invoice {0}?", Invoice.Id); AlertSettings = new AlertSettings(alertMessage, "Verify", AlertSettings.AlertType.YesNo, VerifyInvoiceCreationAction);I’m interested to see if anyone has seen this approach before or can see any major flaws in the logic. Basically, I’m using a generic FrameworkElement Tag property and the FrameworkElement TargetUpdated event handler as a point of entry that triggers a specified action. It seems pretty straightforward.
Here is the full project: MvvmSimple2.zip