I am using MvvmCross to create a MonoTouch application. I have followed the the basic tutorial, and so far so good. The only problem is that my (initial and so far only) view is displayed with a top bar/navigation bar, which I don't want. I am able to hide the navigation bar by calling
this.NavigationController.NavigationBarHidden = true;
in the view controller's ViewDidLoad. I would prefer to not have to suppress the navigation bar, but rather that it not be there at all. The fact that it is appearing is suggesting that perhaps I am doing something wrong/inheriting from the wrong base classes?
Further details on my code:
The view controller inherits from MvxBindingTouchViewController.
My Setup class inherits from MvxBaseTouchBindingSetup (I will not be using TouchDialog anywhere, so am not inheriting from MvxTouchDialogBindingSetup).
Any help would be greatly appreciated! If I need to supply more details on my code, please let me know.
The Navigation bar is part of the UINavigationController which is used in the default Presenter.
The Presenter is the thing that decides how a View (a UIViewController) is shown - whether it is displayed in a popup, displayed as a Modal view, pushed into a navigation controller, etc.
If you want to customise the Presenter - e.g. so that it hides the navigation bar - then just switch in your own implementation in your AppDelegate.cs where you find the code:
// initialize app for single screen iPhone display
var presenter = new MvxTouchViewPresenter(this, _window);
var setup = new Setup(this, presenter);
setup.Initialize();
You can see some examples of custom presenters in the TwitterSearch and Conference samples. TwitterSearch uses different presenters for phone vs tablet; and Conference uses a presenter which is aware of multiple tabs, each of which contains a NavigationController.
There are also a few other questions around on custom presenters like MvvmCross Using a modal ViewController from a Tab and why does MvxModalSupportTouchViewPresenter in MvvmCross only support one modal view
Related
I'm new to the PRISM library. Although it updated quickly, the documentation is confusing, incomplete, and mixes many versions. Currently, I'm using PRISM 8.
I would like some help with an example of how to open multiple windows (modal and non-modal) from main window buttons, sending parameters, and receiving messages, because I was only able to found examples of UserControl View injected into a Window View.
The Prism library does not limit you in any way to use windows like you would in WPF without it. You can write your own window service that suits your requirements to manage instantiating and showing windows. How you implement communication between them is up to you. You could communicate between view models using Prism's event aggregator.
The IDialogService is a feature introduced in Prism 7.2.0.1367, which is a built-it implementation of such a service. The documentation for it is up-to-date and there is not much to add, as it covers everything from creating dialogs, passing parameters, showing dialogs, as well as registering custom windows and styling.
The only thing that changes in Prism 8 is that you can now register multiple dialog windows .
// Default dialog window
containerRegistry.RegisterDialogWindow<MyDialogWindow>();
// Another dialog window that can be accessed by name
containerRegistry.RegisterDialogWindow<MyOtherDialogWindow>(nameof(MyOtherDialogWindow));
You can refer to them by name when showing a dialog with the dialog service.
// Shows the dialog view in them default dialog window
dialogService.Show(nameof(MyView), new DialogParameters(), result => { /* ...handle result.*/ });
// Shows the dialog view in the "MyOtherDialogWindow"
dialogService.Show(nameof(MyView), new DialogParameters(), result => { /* ...handle result.*/ }, nameof(MyOtherDialogWindow));
I would like some help with an example of how to open multiple windows (modal and non-modal) from a main window buttons, [...]
You need to access the IDialogService in your view model. Pass it in the constructor and store it in a field. The dependency container will inject it automatically.
private readonly IDialogService _dialogService;
public MyViewModel(IDialogService dialogService)
{
_dialogService = dialogService;
}
Create a ICommand property in your view model that a button in your view binds to.
OpenDialogCommand = new DelegateCommand(ExceuteOpenDialog);
In the execute method, create the dialog paramaters and show the dialog with Show or ShowDialog (modal).
private void ExceuteOpenDialog()
{
dialogService.Show(nameof(MyOtherView), new DialogParameters(), result => { /* ...handle result.*/ });
}
[...] sending parameters and receiving messages, [...]
That depends on your requirements, but you can have a look at the event aggregator. The documentation is still valid and comprehensive, so nothing to add.
[...] I was only able to found examples of UserControl View injected into a Window View.
That is how the dialog service works. You can use any UserControl to be displayed in a dialog. The dialog service automatically places it in a dialog host window. This way you can reuse your view and make it easier to change and maintain. If you would define your view as Window, you would lose the flexibility of applying different dialog windows without changing the view type and its XAML, as well as the ability to use it anywhere else within other views as component.
When using code behind, the code looks like this:
AnotherWindow x = new AnotherWindow();
x.Show() ;
// or x.ShowDialog()
But how can I achieve this using MVVM? Specifically Prism?
In case you need to build a dialog for asking user login input or progressing dialog, MahApps.Metro can be a useful toolkit as it provides you with some built-in dialog UI/functionalities with MVVM pattern. For more information, check some examples here:
https://mahapps.com/controls/dialogs.html
In Prism, there's the InteractionRequest for short-lived dialogs. If you're looking for a long living dialog, like a second application window or shell, you're stuck with new Window ... Show.
To make your dialog service mvvm-friendly, you should hide it behind an interface and make it as generic as possible. Using view model first here eliminates the need to specify a window type, because you can provide a default window that just contains one large ContentControl, and the view can be mapped as DataTemplate.
I'm currently very stuck with this, my designer wants to have our app with WindowStyle.None to remove the borders and default ugly controls, he has then add custom controls, usually to allow dragging in the past we have used a rectangle and monitored the mousedown event to allow for dragmove.
However with Caliburn micro we lose control of the window because windowmanager create this for us, I'm aware you can override the create window method, but this still doesn't give access to adding UI elements to the window itself and binding to those events. Or at least i can work out a way to do this.
Basically what we are trying to achieve is the "mainwindow" with a WindowStyle.None and that ability to drag and move the window. My googling has failed to give a solid answer on this, and im hoping someone here has an idea.
Caliburn.Micro doesn't force you to make the all your views UserControls. The main view or the one your showing as the main window can be a Window control and you can set properties directly on that Window such as "WindowStyle.None". When Caliburn.Micro sees that the view behind your main view model (the view model you are using as the root, then one you are creating first) is actually a Window and not a UserControl then it will honor this and show that window, It Will Not create a new Window. So you can set your properties directly on that Window and everything shall work fine.
The Caliburn.Micro WindowManager provides overrides to its Show methods that allow you to set the settings of the window that is created.
Have a look here for an example.
Alternatively, you can use a Window directly as your view type (in XAML and the generated code behind file), and set the properties declaratively in the XAML.
If you wish to enable all of your dialogs etc to have common UI components, then you could create a derived WindowManager type that delegates the call to the CM WindowManager but wraps the passed in view model with your common view model. Then register this custom window manager in the bootstrapper rather than the default CM window manager.
In my MainPage.xaml, I created a Pivot Control: <controls:Pivot Title="Powder God" Name="PivotControl">.
My first pivot view is a HubTile that summarize all other individual pages. So my application bar will be different between the first pivot view and all other ones.
That's why I put my application bar in App.xaml's resource section, then load based on selected index of my pivot.
My question is:
In the application bar I will be using for all individual pages, I want to have a delete option, where I will remove that specific item (a view model) from my data context.
I know I can use PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; to access navigation services, but I don't know how can I reference to my pivot, so that I can get the selected index and proceed forward.
Thanks!
Using MVVM you SHOULDN'T do this:
((PageType)Application.Current.RootVisual).PivotControl. //Blah
PageType is whatever type PhoneApplicationFrame is that contains your PivotControl. If this doesn't work you need a Property in the RootVisual
PAGE
public Pivot MyPivot
{
get
{
return PivotControl;
}
}
APP
((PageType)RootVisual).MyPivot. //Blah
On one level Microsoft's suggestion of putting the ApplicationBar in App.xaml is great as it can be referenced from everywhere and would appear to encourage code reuse: however this question highlights the limit to this approach. An application bar is typically used to provide actions which are specific to the current page (or pivot item) and just because the buttons are the same you may not want the exact same code to run in each case.
In this case I think it would better to create a factory method that creates your common ApplicationBar with the click handlers you specify specific to your page/pivot item. For bonus points put the method in a new class (not App) so it doesn't get lost in all the boilerplate code there. Call this factory method in your page constructor and remember your ApplicationBar in your class. For multiple app bars, create them all up front and you can then easily switch between these app bars in your Pivot SelectionChanged code.
The alternative of creating the ApplicationBar in App.xaml and then retrieving this from the App.xaml.cs "Resources" ResourceDictionary in code, modifying the click callbacks, is more complicated in my opinion.
I wish they'd done a better job of implementing the ApplicationBar so people wouldn't want to do this. I've found that using the ApplicationBar forces you to add code to your Page.xaml.cs even if you use a framework like MVVM Light. This is still OK in MVVM as it's UI specific code that belongs in the View, but it makes things inconsistent if you're using ICommand everywhere else. Last time I decided it was better to create the entire ApplicationBar in code rather than hack this kind of thing via App.xaml.cs.
Update: There is a UserVoice request for a data bindable ApplicationBar.
I'm having a problem understanding something about MVVM. My application relies on dialogs for certain things. The question is, where should these childwindows originate from? According to MVVM, viewmodels should contain only businesslogic and have zero actual knowledge about UI. However, what other place should I call my childwindows from, considering they're UI elements?
Doesn't this create tight coupling between elements?
Since you tagged the question with Prism, I'll suggest the way I've done it in the past using Prism. Have the IEventAggregator injected into your ViewModel, and then when you want to pop open the dialog, publish a "ShowDialogEvent" or something like that. Then, have another Module called "DialogModule" or whatever, which upon initialization subscribes to that event, and shows the dialog. Furthermore, if you want to pass data back to the original ViewModel, have the ViewModel of the dialog publish a "DialogCloseEvent" or something like that with a payload of the data you need. You can then subscribe to that event back in your main ViewModel.
See Handling Dialogs in WPF with MVVM
In the past, I have accomplished this by using Unity to resolve a custom interface that has a Show() method and a completed event. Then in the ViewModel I would call IScreen screen = container.Resolve<IScreen>(Resources.EditorWindowKey); and then just call screen.Show();.
The big advantage of this is that I can then just simply change my Unity configuration to remove the view when I'm testing my VM's.
The primary route I've been using to do this is to create a command inside your View layer. That command object accepts a parameter that is the ViewModel object that you want to display. The command then finds the appropriate ChildWindow, creates it and displays it with the parameter set as the content or however you will set it up. This way you can just bind a button's command property to that command, and its commandparameter to the object you want to show in the popup and your ViewModel objects never have to care how it's being displayed.
Prompting for user input (like saving a dirty file or something) doesn't work in this scheme. But for simple popups where you manipulate some data and then move on, this works very well.
The ViewModel sample application of the WPF Application Framework (WAF) demonstrates how to show a Modal Dialog.
I would suggest to use a controller in this scenario, say DI'ed dialogController backed up with a dialog shell. The source viewmodel(ie from where the request to open a dialog is originating) will make a call to dialogController.ShowDialog(<<ViewNameToHostInRegion>>,<<RegionName>>).
In Order to transfer the data to and from the dialog and sourceview you can use MessageBus. So essentially when you invoke the ShowDialog() you populate the messagebus, and when the close command of target View(The view hosted in Dialog shell) invoked - say in "Select" button -- Let the target view add/update the messagebus. So that source view model can work with updated data.
It has got many advantages :
1) Your source view works with dialog controller as BlackBox. ie it doesnt aware of what exactly the Dialog view is doing.
2) The view is being hosted in Dialog Shell -- so you can reuse the dialog again and again
3) Unit testing of source view is limited to test the actual functionality of the current viewmodel, and not to test the dialog view\view model.
One heads-up which I came across with this one is, while creating the test cases you may need to write testable Dialog controller which do not show the actual dialog while running the testcases in bunch. So you will need to write a TestableDialogController in which ShowDialog does nothing (Apart from deriving from IDialogController and provide blank implementation of ShowDialog().
Following is the psudeo code :
LocalMessageBus.AddMessage(<MessageKey>,<MessageActualContentAsObject>);
dialogController.ShowDialog(<TargetViewName_SayEmployeeList>);
Employee selectedEmployee = LocalMessageBus.GetMessage(<MessageKey>) as Employee;
if (selectedEmployee != null)
{
//doSomework with selected employee
}