Caliburn ShowDialog and MessageBox - c#

I'm making a small demo application for MVVM with caliburn.
Now I want to show a MessageBox, but the MVVM way.
For dialogs I created an event, that is handled in the ShellView (the root view)
and just calls WindowManager.ShowDialog with a Dialogs ViewModel type.
Seems to stick to MVVM for me.
But what is the way to show a messagebox and get its result (Okay or cancel)?
I already saw this question, but it contains no answer either.
Mr Eisenberg hisself answers with
"Caliburn has services built-in for calling custom message boxes."
Can anyone tell what he means with that? I don't see it in the samples.

As you mentioned, you just prepare the view model (e.g. ConfirmationBoxViewModel) and an appropriate view. You'll have to create two actions (after inheriting the view model from Screen, which is necessary to use TryClose. You can always implement IScreen instead, but that would be more work):
public void OK()
{
TryClose(true);
}
public void Cancel()
{
TryClose(false);
}
and then in your other view model:
var box = new ConfirmationBoxViewModel()
var result = WindowManager.ShowDialog(box);
if(result == true)
{
// OK was clicked
}
Notice that after the dialog closes, you can access the view model properties if you need to pull additional data from the dialog (e.g. Selected item, display name etc).

In the article A Billy Hollis Hybrid Shell (written by the framework coordinator) the author showed a nice way to handle both dialog and message boxes, but he used dependency injection (you can go without DI of course but it makes things simpler). The main idea is that you can let your main window, the one used as the application shell implement an interface that looks something like this:
public interface IDialogManager
{
void ShowDialog(IScreen dialogModel);
void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);
}
and then he registers this interface with the IoC container, I guess you can use your imagination from there on and if you don't have time then you can look at the source code that accompanies the article.

When the root/main/shell view-model implements a kind of DialogService interface, every other view-model needing to show dialogs will end up with a dependency on the root view-model. Sometimes this might not be desiderable, e.g. if it could cause a dependency loop:
DialogService (aka RootViewModel) -> SomeViewModel -> RootViewModel.
A more involved approach to break this dependency chain (and actually invert it) is the following:
Implement a behavior that detects Window.OnSourceInitialized event and attach it to main view Window component. That is the event fired when the window handle is available. Upon event, behavior will notify some handler passed in via attached property:
<my:WindowSourceBehavior InitListener="{Binding WindowListener}" />
public class WindowSourceBehavior : Behavior<Window>
{
// ...
// boilerplate code for IWindowListener InitListener dependency property
// ...
attachedWindow.SourceInitialized += (sender, evt) =>
{
// ...
InitListener.SourceInitialized(sender as Window);
}
}
DialogService exposes a handler - or interface - as requested by behavior:
public class DialogService : IWindowListener
{
// ...
public void SourceInitialized(Window rootWindow) { /* ... */ }
}
In root view-model, (indirectly) get the DialogService injected as a dependency. During construction, sets view-model bound property, WindowListener, to the DialogService handler/interface:
public MainViewModel(IWindowListener dialogServiceInDisguise)
{
WindowListener = dialogServiceInDisguise;
}
public IWindowListener WindowListener { get; private set; }
Doing so, the DialogService is able to get a hold of root Window, and whichever view-model needs to show a dialog does not create a(n indirect) dependency on main view-model.

Related

(WPF/MVVM) What's the different between an IService and ViewModel?

I wanted to use SaveFileDialog in my ViewModel, But since it's not correct that bind to a View from ViewModel, I searched for ways to do that. But I found a few answers that doesn't completely separate View form ViewModel, Like this:
public interface IOService
{
void IMessageBox(string Message);
string ISaveFileDialog(string DefaultPath);
}
public class IDialog : IOService
{
public void IMessageBox(string Message)
{
System.Windows.MessageBox.Show(Message);
}
public string ISaveFileDialog(string DefaultPath)
{
System.Windows.Forms.SaveFileDialog dg = new SaveFileDialog
{
InitialDirectory = DefaultPath,
Filter = "PDF files (*.pdf) | *.pdf"
};
dg.ShowDialog();
if (dg.FileName == null)
dg.FileName = string.Empty;
return dg.FileName;
}
}
They said that, this is a Service and using it will separate View from ViewModel. But we have make an Instance from this in ViewModel:
IDialog iDialog = new IDialog();
So I wanna know, What's the diffrence between this method and calling MessageBox or SaveFileDialog from ViewModel directly?
Note: Also I find something that said I could use a Service like the above, But implement it like this:
public class ExportViewModel : BaseViewModel
{
IOService _IOService;
public ExportViewModel(IOService ioservice)
{
_IOService = ioservice;
.
.
}
}
But I don't know how send IOService as a parameter to ExportViewModel (Because we can't create Instances from an Interface!)
You shouldn't pop up dialogs directly from your VM for automated testability.
If you call MessageBox.Show(), your test will get stuck until a person closes the dialog.
If, instead, you use "IMessageBox", for unit tests, you can inject an implementation that doesn't actually show the dialog, but rather returns a specific value (the result).
It's an abstraction used to separate UI concerns from the view model, and to allow you a means to intercept these calls within unit tests. Intercepting these calls allows you to both prevent showing a dialog during a test (which will block execution of your tests, not good) and verify the view model is acting as expected.
There is nothing special about calling it a "Service" or calling the abstraction "IService". It's just an allusion to a common pattern
I need to do something
I can't do it myself
I'll find a service I can hire to do it
I found a service that can do this for me
This service will do it for me
You could just as well call it "IDontTouchUIStuffInMuhViewModel", but that's not as elegant.
Your second problem is solved trivially at runtime by providing an implementation of the interface to your view model. Your view model shouldn't care how it's implemented (polymorphism), so you can pass in a legit implementation at runtime and a fake one during testing.
You can also accomplish this by using Dependency Injection or Inversion of Control. By using a library such as Unity to instantiate your view models, all dependencies are automatically provided, based on how you configured the container.

mvvmcross IOS: How to callback from a ViewModel to a View

I have a MvxViewController and in the ViewDidLoad i bind the button click to the viewmodel. When the button is clicked I open another view in which I will need to return a string back to my first view
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand);
set.Apply();
}
public ICommand MyButtonCommand
{
get
{
_myButtonCommand = _myButtonCommand ?? new MvxCommand(MyButtonCommandClick);
return _myButtonCommand;
}
}
private void MyButtonCommandClick()
{
ShowViewModel<ViewModelNumber2>();
}
After some logic is ran in my second view I want to return the string
private void SomeMethodInViewModelNumber2()
{
//Raise event that will get pickup up in MyView
//Or somehow get "SomeString"
if (OnMyResult != null)
OnMyResult ("SomeString");
}
The problem is that I don't want to send the string back using the messenger. I have my reasons but basically because ViewModelNumber2 can be opened from many different places and works slightly different and managing the different messages that would need to be sent back and where to subscribe to these messages would be a mess
Is there any way that I can do something like the below?
public override void ViewDidLoad ()
{
var set = this.CreateBindingSet<MyView1, MyView1ViewModel>();
set.Bind(myButton).To(vm => vm.MyButtonCommand).OnMyResult((myString) => {Process(myString)});
set.Apply();
}
Or perhaps when I create ViewModelNumber2 I should pass a callBack into the constructor and use that to send the string back from ViewModelNumber2 to MyView1ViewModel
ShowViewModel<ViewModelNumber2>(OnMyResult);
What is the best way to do this?
In short: I don't know what "the best way to do this" is.
The area of ChildViewModel-ParentViewModel messages is complicated - especially because on platforms like Android using Activities and WindowsPhone using Pages you have no guarantee that the ParentViewModel will be in memory when the Child is shown. (Note: this isn't a problem on iOS as its "app suspension" model is simpler)
When I do need one ViewModel returning data to another, then:
Often I try to implement the data collection views as "popup dialogs" rather than as "whole pages" - this makes the parent-child ViewModel relationship more correct - and ensures the parent ViewModel will be in memory when the child closes.
Often I recommend people use a Messenger-based technique like Greg describes in: http://www.gregshackles.com/2012/11/returning-results-from-view-models-in-mvvmcross/
often I've done this messaging via background services rather than via ViewModel-ViewModel messaging (a bit like the way screens are updated in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/tree/master/N-17-CollectABull-Part6)
Another solution I've used is to:
implement a IDropBoxService singleton - with an API like void Deposit(key, value) and bool TryCollect(key, out value)
allow the closing "child" ViewModels to leave "values" when they close
implement IVisible functionality in my "parent" ViewModel - like in https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/master/N-42-Lifecycles/Lifecycle.Core/ViewModels/FirstViewModel.cs#L10
use the IVisible method to check for messages
To implement anything perfectly, you really should add serialisation code to make sure this all works during "tombstoning" on all platforms... but often this is overkill - for a simple data collection dialog users often don't need "perfect" tombstoning support.

Opening new window from ViewModel

How should I be opening new windows? I'm currently doing the following.
EventArgs:
public class GenericViewRequestedEventArgs : EventArgs
{
public GenericViewModel ViewModel { get; private set; }
public GenericViewRequestedEventArgs(GenericViewModel viewModel)
{
ViewModel = viewModel;
}
}
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
private RelayCommand _viewSpecificCommand;
public ICommand ViewSpecificCommand
{
get
{
if (_viewSpecificCommand == null)
_viewSpecificCommand = new RelayCommand(x => viewSpecific());
return _viewSpecificCommand;
}
}
public EventHandler<GenericViewRequestedEventArgs> GenericViewRequested;
private void RaiseGenericViewRequested(GenericViewModel viewModel)
{
var handler = GenericViewRequested;
if (handler != null)
handler(this, new GenericViewRequestedEventArgs(viewModel));
}
private void viewSpecific()
{
RaiseGenericViewRequested(_specificViewModel);
}
}
View:
public partial class MainWindow : Window
{
private void OnGenericViewRequested(object sender, GenericViewRequestedEventArgs e)
{
GenericWindow window = new GenericWindow(e.ViewModel);
window.ShowDialog();
}
}
This does work, but it seems like a lot of code and I end up with code behind in my view any ways.
What's the logic behind sending the command to the viewmodel at all?
Is it just to optionally use the predicate(if so why not bind to Enabled) and possibly avoid exposing additional viewmodels as properties?
Should I be attaching simple event handlers in the XAML(e.g. Click="btnViewSpecific_Click")?
It depends on how "strict" you want to follow the MVVM pattern. This is just one of the basic pitfalls of MVVM and you can solve it depending on your preferences. One way is to simply use the code-behind, but then how will you handle application-wide commands, keyboard shortcuts, etc? It is a bit too short-sighted IMHO.
I think you should at least consider using existing frameworks that have solved these issues for you years ago and will provide you with a solid base for your application.
For example, Catel has a IUIVisualizerService that can show windows based on a view model. The major advantage is that you can push data into the view model and respond to the result as a service. Another nice advantage is that you can mock the IUIVisualizerService so you can test the reacting code on different results provided by the dialog.
** Disclaimer **
I am the developer of Catel, so I have explained the Catel way here. If anyone else wants to comment on other frameworks, feel free to comment or create a new answer.
Yes, there are a lot of additional codes for MVVM. Building a command that independent of Views is usually for unit testing, such that the command and ViewModel can be unit tested without involving UI components.
However, if the "command" is just opening a window, it is not worth to create a command, and unit test the command to see if the GenericViewRequested is really fired(you can even check if the correct _specificViewModel is returned). The codes are far more complicated and just little value is added. Just open the window in View's button click event handler and it is fine.
If you want to see good example, see how this works in the ViewModel (EmailClient) sample application of the WPF Application Framework (WAF).

Updating MainWindow Property from separate MVVM Module

I am looking to implement a "JQuery" style spinner in a WPF application. As this spinner will be triggered from some separate PRISM modules and I want the spinner to cover the whole application window I need to get access to a property in the MainWindow.
I have a property in the MainWindow but I am not able to see it from the other modules.
I have tried Application.Current.MainWindow but no luck.
I have also tried using the Application.Current.Properties[] but I do not know how to trigger a OnPropertyChanged event.
Can someone point me in the right direction please.
Update:
Here are some screenshots and a better description of what I want to do.
Ok, here is an example of my issue. I have an application that contains the following:
WPFApp
WPFApp.Module1
WPFApp.Module2
WPFApp.Module3
WPFApp.Module4
The WPFApp MainWindow contains 2 regions, a Menu region (on the left hand side) and a Content region.
Each module contains 2 views:
A menu view, which is loaded into the MainWindow Menu region
A content view, which is loaded into the MainWindow Content region
In each of the Module content views I want to perform a task that will take a few seconds and while the task is being performed I want to show a “Ajax Style” spinner that will cover the whole application window.
To have used the spinner class detailed here: WPF Spinner via MVVM & Attached Properties
I have been able to use this by adding the AsyncNotifier.Trigger (detailed in the link above) to each of the modules content view, see below.
My problem is:
If I want the spinner to cover the whole application window then I need to add the AsyncNotifier.Trigger to the MainWindow. I also need to expose a property from the MainWindow responsible for showing the spinner and be able to access this from each of the modules.
Any ideas on how to do this?
Update:
Ok, I think I might be getting a bit further but still a little bit stuck on how everything fits together.
I have created my Interface and put it into my Infrastructure module so every other module can access it.
I am using the following piece of code to load my modules from my AggregateModuleCatalog class.
/// <summary>
/// this.Catalogs is a readonly collection of IModuleCatalog
/// Initializes the catalog, which may load and validate the modules.
/// </summary>
public void Initialize()
{
foreach (var catalog in this.Catalogs)
{
catalog.Initialize();
}
}
My problem is that I am unsure of where I should put the SpinnerViewModel? Should it be in my Main project.
Also, where should I pass in the singleton using Constructor injection?
I would look at implementing the mediator pattern here. This is already present in Prism in the form of the EventAggregator
Basically, it sounds like you want to publish control messages of a certain type to any subscribers - in this case you have:
Subscribers
MainWindow
Publishers
Module 1
Module 2
Module 3
The modules should publish a message to signal that they want to 'busy' the MainWindow whilst work is being done/module is being loaded etc
The key thing is that they should not be aware of the MainWindow in any way. This keeps the solution decoupled, MVVM friendly, reusable and easily maintained. This method uses dependency injection, which is usually a good idea :)
In order for this to work, the modules need to take a dependency on the EventAggregator service (there is an IEventAggregator interface in PRISM which is your service interface) and should then publish messages to it
First you need an event which will serve as the message class. Obviously this needs to be visible from both the publisher and subscriber, so may need to sit in an external reference. This needs to inherit from CompositeWpfEvent and you should provide a generic argument for the message payload.
public class BusyUserInterfaceEvent : CompositeWpfEvent<bool>
{
}
(I suppose you could just directly use CompositeWpfEvent<bool> for this which may work equally as well but is not specific enough if you decide to include more event types which use bool as payloads)
Then you need to publish events of the above type from your module
public class Module1
{
private IEventAggregator _eventAggregator;
public Module1(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
public DoSomeWork()
{
// Busy the UI by publishing the above event
_eventAggregator.GetEvent<BusyUserInterfaceEvent>().Publish(true);
}
public FinishDoingSomeWork()
{
// Unbusy the UI by publishing the above event with 'false'
_eventAggregator.GetEvent<BusyUserInterfaceEvent>().Publish(false);
}
}
The MainWindow should also take a dependency on the EventAggregator service and should subscribe to any messages of a certain type. If work is to be done on the UI thread (as it is in this case) you should subscribe using ThreadOption.UIThread
public class MainWindow
{
private IEventAggregator _eventAggregator;
public MainWindow(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
// Subscribe to any messages of the defined type on the UI thread
// The BusyUserInterface method will handle the event
_eventAggregator.GetEvent<BusyUserInterfaceEvent>().Subscribe(BusyUserInterface, ThreadOption.UIThread);
}
public BusyUserInterface(bool busy)
{
// Toggle the UI - pseudocode here!
TickerActive = busy;
}
}
Whilst I have used this pattern in a couple of frameworks successfully before, I admit that I haven't used PRISM so the above code might not be quite right, however, the docs seem concise:
http://msdn.microsoft.com/en-us/library/ff921122.aspx
Using this pattern decouples any components, so it's easy to stop the MainWindow from subscribing to the events and to move the subscriber to a parent component if needs be without touching the implementation of the publishers.
This means you could swap the MainWindow for a completely different window and load your modules in during some part of your application lifecycle, and as long as it was subscribing to these event types it would still busy/unbusy based on the message publishing from the modules. It's a very flexible solution.
PRISMs implementation also provides subscription filtering so you can selectively listen for events.
Additionally, since you are aggregating events there's a lot you can do with this to allow inter-module communication. The only challenge you have is making sure everyone is aware of the message types, which is why an external dependency may be needed to hold the message types
Am I missing something? Should this be as easy as this?
App.xaml.cs
public partial class App
{
public static Window MainWindow {get;set;}
}
MainWindow.xaml.cs
public MainWindow()
{
App.MainWindow = this;
InitializeComponent();
}
public void Spinner(bool show)
{
// Your code here
}
Now in MVVM, the idea would be the same, just don't do it in code behind. You could create a singleton (Singleton has a static instance) for the spinner to bind to and then any module can mess with the singleton property.
If you like Dependency Injection, create an interface, create an object that implements that interface, have the Spinner bind to that object. Then inject that object as the interface into the modules when they are loaded.
Here is a singleton ViewModel that implements an interface. Your Prism projects need to know only about the interface. But your code that loads your Prism modules should know about both the interface and the singleton. Then when the Prism Modules are loaded, the singleton would be passed in (feel free to use whichever injection method is easiest: constructor injection, method injection, or property injection).
using System.ComponentModel;
namespace ExampleCode
{
/// <summary>
/// Interface for managing a spinner
/// </summary>
public interface IManageASpinner
{
bool IsVisible {get;set;}
// Add any other properties or methods you might need.
}
/// <summary>
/// A singleton spinner ViewModel for the main window spiner
/// </summary>
public class SpinnerViewModel : INotifyPropertyChanged, IManageASpinner
{ #region Singleton and Constructor
/// <summary>
/// Singleton instance
/// </summary>
public static SpinnerViewModel Instance
{
get { return _Instance ?? (_Instance = new SpinnerViewModel()); }
} private static SpinnerViewModel _Instance;
/// <summary>
/// Private constructor to prevent multiple instances
/// </summary>
private SpinnerViewModel()
{
}
#endregion
#region Properties
/// <summary>
/// Is the spinner visible or not?
/// Xaml Binding: {Binding Source={x:Static svm:SpinnerViewModel.Instance}, Path=IsVisible}
/// </summary>
public bool IsVisible
{
get { return _IsVisible; }
set
{
_IsVisible = value;
OnPropertyChanged("IsVisible");
}
} private bool _IsVisible;
// Add any other properties you might want to include
// such as IsSpinning, etc..
#endregion
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
}
Hope you can connect the dots from here.
Added Feb 12
Maybe use Method injection? I am not sure you can though.Can you edit the IModuleCatalog interface or the Catalog class? If not, skip this and go to the next idea.
foreach (var catalog in this.Catalogs)
{
catalog.Initialize(SpinnerViewModel.Instance);
}
Maybe your Catalogs should implement a second interface and use property injection instead of constructor injection.
public interface IHaveASpinner
{
public IManageASpinner Spinner {get;set;}
}
Now, have all your catalogs that implement IModuleCatalog also implement IHaveASpinner.
Then have your code do this:
foreach (var catalog in this.Catalogs)
{
catalog.Initialize();
var needSpinner = catalog as IHaveASpinner;
if (needSpinner != null)
{
needSpinner.Spinner = SpinnerViewModel.Instance;
}
}
Where I should put the SpinnerViewModel?
I would suggest your main project. It depends on your design. Do all the modules already reference your main project? If so, then put it in your main project. If not, then perhaps there is a project the main project and all the modules already reference? You could even create a separate project/dll specific for your spinner, but only if you have to as that might be overboard unless you have a strict design with rules for what can reference what.
You shouldn't need a VM just for the spinner. What I do is have the spinner itself (animation, icon, whatever) in my main window's view, and have the main window VM implement the following interface:
public interface IApplicationBusyIndicator
{
bool IsBusy { get; set; }
}
Using your favourite IoC container (I use Castle), I just inject IApplicationBusyIndicator into other VMs. Whenever one of these starts some long-running task it just sets IsBusy to true (then back to false when it's finished). The busy indicator in the main window view has its Visibility property bound to the main window VMs IsBusy property (using a boolean-to-visibility converter of course).
If, in your scenario, there is some architectural constraint preventing VMs in other modules from being injected with an instance of the main window VM, then you could use Prism's event aggregator to publish a message to say "application busy", and have the main window VM subscribe to the event, and show the busy indicator.
If you are looking to create more of some "visual" style / interface of a spinner, I suggest looking into creating your own subclass spinner (if you need special functionality behind it), and then create your own customized style for its look / behavior. Then when you put the spinner on the forms, just use a spinner of the class/style you designate and they should function for you well.
Here is one of my own learning about styles, but simple label
Another link where I helped someone walking through creating their own custom class/style which MIGHT be what you are trying to globally implement.
And a link to template / style defaults to learn from

XAML C# Function across multiple windows

I've got a program I am working on that has multiple windows. The windows are similar in functionality and I want to have a single event handler to cover a button press event for each window in the application. Is this possible?
If you need to bind a handler in code behind you can encapsulate a handler by delegate and inject into the Windows which are required it.
For instance using Action<T>:
Action<string> commonHandler = (parameter) =>
{
// handler code here
};
class MyWindiow
{
public MyWindiow(Action<string> handler)
{
// store to local and assign to button click
// button.CLick += (o, e) => { handler(parameterToBepassed); }
}
}
I'd look into using a framework to help you out here. My favorite is Prism v4.
If you follow the M-V-VM design pattern you're life will be a lot easier. You'll need to understand Data Binding and DataContext.
That being said, if you decide to go this path, you can bind each of your windows to a command:
<Button Command="{Binding DoFooCommand}" Content="DoFoo"/>
You're ViewModel would have a DelegateCommand member to execute.
public class SomeViewModel : NotificationObject
{
public SomeViewModel()
{
DoFooCommand = new DelegateCommand(ExecuteFoo);
}
public DelegateCommand DoFooCommand { get; set; }
private void ExecuteFoo()
{
//Use the EventAggregator to publish a common event
}
}
And finally, somewhere else in your solution you'll have a code file/class that subscribes to the event and waits for someone to publish the event to process it.
public class SomeOtherPlace
{
public SomeOtherPlace()
{
//Use the EventAggregator to subscribe to the common event
}
public void FooBarMethodToCallWhenEventIsPublished(SomePayload payload)
{
//Do whatever you need to do here...
}
}
I realize some of the things were left out (such as what a "SomePayload" is... look into the EventAggregator information), but I did not want to get into it too much. Just give you a guide on where to go for information and some base code to work off of. If you decide to use the EventAggregator then you'll need to ensure that your subscribing call and publishing calls are utilizing the SAME instance of the EventAggregator. You can do this by looking into MEF. Prism is setup to work with MEF perfectly... I'm not going to lie. Doing all this requires a bit of a learning curve, but it will be worthwhile in the end when you can unit test your ViewModels easily and have your code loosely coupled. The EventAggregator is a great way for different classes to communicate to each other without relying on knowing about each other. And MEF is great for having a Container for your services that you want to utilize across your application.
Hope that gave you a bit of insight on how to go about doing what you want to do on the correct path.

Categories

Resources