I have an MVVM Cross application, and I'm trying to figure out why the initial screen isn't showing (this is on WinRT).
The following code looks like it loads the ViewModel MainViewModel:
var setup = new Setup(rootFrame);
setup.Initialize();
var start = Cirrious.CrossCore.Mvx.Resolve<Cirrious.MvvmCross.ViewModels.IMvxAppStart>();
start.Start();
However, the Start method of MainViewModel never gets called. The reason appears to be this error in the output:
Error seen during navigation request to MainViewModel - error KeyNotFoundException: Could not find view for MyApp.PCL.ViewModels.MainViewModel
So, here's my guess as to the problem: it's looking for MyApp.PCL.ViewModels.MainView, Obviously, this doesn't exist. The view is in: MyApp.Views.MainView. Is my assumption correct and, if so, how can I persuade MVVMCross to look in the correct place?
EDIT:
The Setup method is called from the MyApp.Windows WinRT app.
protected override IMvxApplication CreateApp()
{
return new MyApp.PCL.App();
}
MyApp.PCL is a portable class library that is referenced from MyApp.Windows.
MyApp.PCL contains the ViewModels, and MyApp.Windows contains the views.
I have tried to override Start():
public class MainViewModel
: MvxViewModel
{
public override void Start()
{
System.Diagnostics.Debugger.Break();
base.Start();
}
but this never gets hit.
Massive Doh! here, but I'll post the solution; my view was not using the MvxStoreView, but the standard StoreView.
The solution was to change the view as follows:
...
xmlns:views="using:Cirrious.MvvmCross.WindowsStore.Views"
...
<views:MvxStorePage
Related
I'm a complete newbie to Android programming.
I've chosen MvvmCross 5.x as my framework.
I think I understand how to send both a 'complex' object and a dictionary (bundle) from a ViewModel:
Task Navigate<TParameter>(IMvxViewModel<TParameter> viewModel,
TParameter param,
IMvxBundle presentationBundle = null);
How do I receive the bundle in the ViewModel being navigated to?
That is, what method is called with the dictionary/bundle?
The presentationBundle parameter you used for navigation is part of the ViewModelRequest object, which is passed to the Show method of your MvxViewPresenter. You can create a custom implementation of a view presenter on your platform and use the PresentationBundle to customize for example how the navigation transition looks or perhaps to modify the navigation stack.
See the ViewModelRequest source code on GitHub. The property PresentationValues will contain the bundle you passed in the navigation. Following is an example implementation of custom view presenter on Android:
class CustomViewPresenter : MvxAndroidViewPresenter
{
public CustomViewPresenter(IEnumerable<Assembly> androidViewAssemblies)
: base(androidViewAssemblies)
{
}
public override void Show(MvxViewModelRequest request)
{
if (request.PresentationValues.ContainsKey("something"))
{
//handle presentation value
}
base.Show(request);
}
}
There are more methods you can override in the presenter that use ViewModelRequest and where you can then access the PresentationValues property. These are specific for each platform and depend on the type of navigation being performed. On Android you have ShowActivity, ShowDialogFragment, ShowFragment, etc. You can see the default implementation of MvxAndroidViewPresenter on MvvmCross Github as well.
You can see a nice example of how a custom ViewPresenter with PresentationBundle can be used in this blog post by the awesome Greg Shackles. He also has a nice introduction to view presenters.
My history:
I'm developing a WPF application, which will run in full screen on the touch screen. Navigation in my application can be done only by clicking a button on each page ("back" or "logout").
This is not a Universal App, but it looks like.
Assumptions of the project:
Application will run on full screen mode in Windows 7 on the touch screen.
I'm using Caliburn.Micro MVVM framework.
Problem and question:
I've got 1 window and 3 UserControl (and ViewModels) Concept art
Window ShellView
UserControl LoginView
UserControl OrdersView
UserControl OrderDetailView
When application starting, i'm set LoginView as default and load it by using CM Conductor ActivateItem method, but i don't know how to set another View from UserControl like LoginView
I have read: this question but this doesn't cover my case
and this answer but it's to hard to understand for me.
My ideas:
make static method in ShellViewModel like:
ShellViewModel
public static void setOrdersView() {
ActivateItem(new OrdersViewModel());
// Error : An object reference is required for the non-static field, method, or property 'Caliburn.Micro.ConductorBase<object>.ActivateItem(object)
}
ShellViewModel.setOrdersView();
make listener in ShellViewModel and send event from child ViewModel ( but now i don't know how to achieve it)
Question: What is the best way to handle navigation in this case?
Application architecture:
ShellView
<Window>
<ContentControl x:Name="ActiveItem" />
</Window>
ShellViewModel
public class ShellViewModel : Conductor<object>, IShell
{
public ShellViewModel()
{
LoadDefault();
}
public void LoadDefault()
{
ActivateItem(new LoginViewModel());
}
}
LoginView
<UserControl>
<Button x:Name="Login" />
</UserControl>
LoginViewModel
public class LoginViewModel : PropertyChangedBase
{
public void Login() {
if (LoginManager.Login("User", "Password")) {
// How to redirect user to OrdersView?
}
}
}
I have similar applications with one shell window and many activated views inside and some dialog windows.
You should use EventAggregator pattern for these needs and Caliburn already has implementation.
How to Achieve:
Minimum Shell signature
public class ShellViewModel : Conductor<object>,
IHandle<ChangePageMessage>,
IHandle<OpenWindowMessage>
You need two fields inside (second one is for dialogs):
public IEventAggregator EventAggregator { get; private set; }
public IWindowManager WindowManager { get; private set; }
I've set single instances of that objects through IoC. You can also define them as Singletons.
EventAggregator needs subscription on object where IHandles are implemented.
EventAggregator.Subscribe(this); //You should Unsubscribe when message handling is no longer needed
Handlers implementation:
public void Handle(ChangePageMessage message) {
var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
ActivateItem(instance);
}
public void Handle(OpenWindowMessage message) {
var instance = IoC.GetInstance(message.ViewModelType, null);//Or just create viewModel by type
WindowManager.ShowWindow(instance);
}
Messages for event aggregator can be only marker classes but sometimes it is useful to pass more parameters like ours OpenWindowMessage and ChangePageMessage classes - they are absolutely similar by content, so for example:
public class OpenWindowMessage {
public readonly Type ViewModelType;
public OpenWindowMessage(Type viewModelType) {
ViewModelType = viewModelType;
}
}
All your viewModels can also subscribe to EventAggregator instance and Handle some messages for communication, or even for initial parameters. I have something like MyViewModelInitMessage classes almost for every viewModel, and just use two publish methods together.
EventAggregator.Publish(new ChangePageMessage(typeof(MyViewModel)));
EventAggregator.Publish(new MyViewModelInitMessage("...all needed parameters"));
So when I publish those two - mine ViewModel will be activated, then it subscribes to EventAggregator(don't forget to do that or second message handling never occurs), and will handle its InitMessage right after.
Now with EventAggregator you can send messages between all ViewModels that are currently subscribed to it.
That seems pretty common solution.
I am taking over an application developed with MvvmCross.vNext.
While trying to update it with MvvmCross.V3, I found the following breaking change: in the constructor of the MainViewModel, we show the LoginViewModel (ShowViewModel()).
It worked fine in vNext.
But with V3, the LoginView doesn't show.
After a long search, I found out that the following code, added in MvxStoreMainThreadDispatcher.RequestMainThreadAction :
if (_uiDispatcher.HasThreadAccess)
{
action();
return true;
}
was responsible for my troubles.
If I comment it out, my application works as previously, but I guess this code is there for some reasons...
Do you have any suggestions ?
Can I force the previous behavior without changing MvvmCross source code?
Should I refactor the code to handle the LoginView differently ?
Thanks in advance for your comments.
Philippe
While trying to update it with MvvmCross.V3, I found the following breaking change: in the constructor of the MainViewModel, we show the LoginViewModel (ShowViewModel()). It worked fine in vNext.
I think your constructor navigation would have broken on several platforms in any MvvmCross version. To be honest, I think you were lucky it worked before.
The problem is that ViewModels are constructed (or located) during View events such as ViewDidLoad, OnNavigatedTo, and OnCreate - and these events are normally
called during 'page transitions'
To work around this you will need to move your login navigation out of the constructor.
How you do this depends on your app
if you do need the Home->Login backstack, then you can trigger off some async or time delay or you can trigger off some other View event such as something like ViewDidAppear
if you don't need that backstack then the way I normally implement this sort of thing is to use a custom IMvxAppStart - something like:
public class AppStart
: MvxNavigatingObject
, IMvxAppStart
{
public void Start(object hint = null)
{
var authService = Mvx.Resolve<IMySerice>();
if (authService.IsLoggedIn)
{
ShowViewModel<HomeViewModel>();
}
else
{
ShowViewModel<LoginViewModel>();
}
}
}
(you can see another example in https://github.com/slodge/MvvmCross/blob/v3/Sample%20-%20CirriousConference/Cirrious.Conference.Core/ApplicationObjects/AppStart.cs)
This can be registered in App.cs startup using:
RegisterAppStart(new AppStart());
Technologies
C# 4.0
Prism 4 with Unity for DI
WPF
MVVM
Preface
There are two projects in my solution, MyApp.Shell and MyApp.ModuleFoo
MyApp.Shell's Unity Bootstrapper
protected override IModuleCatalog CreateModuleCatalog()
{
// Module assemblies are read from a directory.
DirectoryModuleCatalog moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = #".\Modules";
return moduleCatalog;
}
The project MyApp.ModuleFoo contains a View and a View Model.
The ViewModel
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
{
public string FooText
{
get { return "Foo!"; }
}
}
The View
<Label Content={Binding FooText} />
The View's Code-behind
// Unity automatically sees this as Constructor Injection,
// which is exactly what I desire.
public FooView(FooViewModel viewModel)
{
DataContext = viewModel;
...
}
MyApp.FooModule's Initialization
Perhaps registering FooView with the region manager is inadvertently registering FooViewModel with Unity?
public void Initialize()
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("FooRegion", typeof(FooView));
}
The view correctly displays "Foo!".
Problems
How do I tell Unity to register only a single instance of FooViewModel?
Additionally, (and I'm thinking ahead here), how would I tell unity not to register FooViewModel?
Thanks for the help.
Edit:
Added MyApp.FooModule's Initialization code
Edit (Solution):
It turns out RegisterViewWithRegion has two overloads. From Prism's documentation, when the overload I'm using is used, a new instance of the view is created. I'm assuming this also creates a new instance of FooViewModel.
The other overload uses a delegate to resolve FooView. The documentation says this overload is used in the "ViewModel-first" approach. I'm going to make this question as answered, but if anyone has any additional insight, I'd love to hear.
// Somehow, Unity sees this class and registers the type.
public class FooViewModel : ViewModelBaseClass
...
I am surprised that you say this as Unity does not register types inside the container by default. You have to tell it to do so either programmatically or in the config file.
When you have concrete classes (not interfaces) they will automatically get created by Unity whether they are registered or not. If not the default behavior is to create a new instance each time. No lifetime management is applied also.
As far as your questions:
To register only one type within your initialisation of your module just have.
Container.RegisterType<FooViewModel>(new ContainerControlledLifetimeManager());
The lifetime manager will instruct unity to only create one instance of the view model.
I've been trying to inject the modules from my ModuleCatalog into my Shell's ViewModel but I'm not having much luck...
I'm creating the ModuleCatalog in my Bootstrapper and my module is getting onto the screen from its Initializer without problem. However, I'd love to be able to bind my list of modules to a container with a DataTemplate which allowed them to be launched from a menu!
Here's my Boostrapper file, I'll be adding more modules as times goes on, but for now, it just contains my rather contrived "ProductAModule":
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
protected override IModuleCatalog GetModuleCatalog()
{
return new ModuleCatalog()
.AddModule(typeof(ProductAModule));
}
protected override DependencyObject CreateShell()
{
var view = Container.Resolve<ShellView>();
var viewModel = Container.Resolve<ShellViewModel>();
view.DataContext = viewModel;
view.Show();
return view;
}
}
Following on from that, here's my Shell's ViewModel:
public class ShellViewModel : ViewModelBase
{
public List<IProductModule> Modules { get; set; }
public ShellViewModel(List<IProductModule> modules)
{
modules.Sort((a, b) => a.Name.CompareTo(b));
Modules = modules;
}
}
As you can see, I'm attempting to inject a List of IProductModule (to which ProductAModule inherits some of its properties and methods) so that it can then be bound to my Shell's View. Is there something REALLY simple I'm missing or can it not be done using the Unity IoC? (I've seen it done with StructureMap's extension for Prism)
One more thing... When running the application, at the point the ShellViewModel is being resolved by the Container in the Bootstrapper, I receive the following exception:
Resolution of the dependency failed, type = "PrismBasic.Shell.ViewModels.ShellViewModel", name = "". Exception message is: The current build operation (build key Build Key[PrismBasic.Shell.ViewModels.ShellViewModel, null]) failed: The parameter modules could not be resolved when attempting to call constructor PrismBasic.Shell.ViewModels.ShellViewModel(System.Collections.Generic.List`1[[PrismBasic.ModuleBase.IProductModule, PrismBasic.ModuleBase, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]] modules). (Strategy type BuildPlanStrategy, index 3)
Anyway, simple huh... Looks bemused...
Any help would be greatly appreciated!
Rob
I think you could probably just do this:
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
Container.RegisterType<IProductModule>();
base.ConfigureContainer();
}
private static ObservableCollection<IProductModule> _productModules = new Obser...();
public static ObservableCollection<IProductModule> ProductModules
{
get { return _productModules; }
}
protected override IModuleCatalog GetModuleCatalog()
{
var modCatalog = new ModuleCatalog()
.AddModule(typeof(ProductAModule));
//TODO: add all modules to ProductModules collection
return modCatalog;
}
...
}
Then you would have a static property that anything could bind to directly, or could be used from your ViewModel.
Here is how to get a list of module names that have been registered with the module catalog.
public class MyViewModel : ViewModel
{
public ObservableCollection<string> ModuleNames { ... }
public MyViewModel(IModuleCatalog catalog)
{
ModuleNames = new ObservableCollection<string>(catalog.Modules.Select(mod => mod.ModuleName));
}
}
That's pretty much it. IModuleCatalog and IModuleManager are the only things that are setup in the container for you to access in terms of the modules. As I said, though, you won't get any instance data because these modules (hopefully) are yet to be created. You can only access Type data.
Hope this helps.
I think you misunderstood the purpose of the modules. The modules are just containers for the views and services that you wish too use. The shell on the other hand should just contain the main layout of your application.
What I think you should do is to define a region in your shell, and then register the views (which in your case are buttons) with that region.
How you wish do deploy your views and services in terms of modules is more related to what level of modularity you're looking for, i.e. if you want to be able to deploy the views and services of ModuleA independently of the views and services of ModuleB and so on. In your case it might be enough to register everything in one single module.
Take some time to play around with the examples provided with the documentation, they are quite good.
The reason why your examples throws an example is because your ShellViewModel is depending on List and that type is not registered in Unity. Furthermore you're registering IProductModule with Unity, which makes no sense because an Interface cannot be constructed.
I think I encountered a similar problem today, it turns out that PRISM creates the shell before initializing the modules, so you can't inject any services from the modules into the shell itself.
Try creating another module that depends on all of the others and implements the functionality you want, then you can add it to a region in the shell to display your list of services. Unfortunately I haven't had a chance to try it yet, but this is the solution I plan on implementing.
As a side note, I think you need to mark the property with an attribute to use property injection, but I could be mistake (it's been a while since I played with Unity directly).
Edit: You need to apply the DependencyAttribute to properties to use setter injection in Unity; you can read about it here.
var modules = new IProductModule[]
{
Container.Resolve<ProductAModule>()
//Add more modules here...
};
Container.RegisterInstance<IProductModule[]>(modules);
That's it! Using this code, I can inject my modules into the ShellViewModel and display each module as a button in my application!
SUCH a simple resolution! From a great guy on the CompositeWPF Discussion group. I recommend them without reserve ^_^