Using MVVM Light in a WPF MVVM application.
I want to use Ninject instead of SimpleIOC.
Even in a brand new WPF/MVVM Light v4 project, I get a null reference for MainViewModel when the Main Property in the ViewModelLocator is called by the XAML.
private static readonly StandardKernel kernel;
static ViewModelLocator()
{
if (ViewModelBase.IsInDesignModeStatic)
{
}
else
{
kernel = new StandardKernel(new mymodule());
}
}
public MainViewModel Main
{
get { return kernel.Get<MainViewModel>(); }
}
MyModule looks like this:
public class mymodule:NinjectModule
{
public override void Load()
{
Bind<MainViewModel>().ToSelf();
}
}
I've also tried
public class mymodule:NinjectModule
{
public override void Load()
{
Bind<MainViewModel>().To<MainViewModel();
}
}
Ninject kernel's .Get<T> does not return null.
Except in case you explicitly tell it to by doing:
Bind<T>().ToConstant(null);
Bind<T>().ToMethod(x => null);
Bind<T>().ToProvider<TProvider>() --> and TProvider.Create(...) returns null
It's very unlikely you have any of these.
So if there's a NullReferenceException when accessing the Main property, it must be because private static readonly StandardKernel kernel is null.
Now if the code you've provided us is a Minimal, Complete, and Verifiable example, that means ViewModelBase.IsInDesignModeStatic returns true.
Related
I'm trying to pass a service to a component constructor in MAUI:
public partial class MyComponent: ContentView
{
public MyComponent(MyService service)
{
InitializeComponent();
data = service.getData();
}
}
But it throws error and asks to add a default constructor:
public partial class MyComponent: ContentView
{
public MyComponent() { }
public MyComponent(MyService service)
{
InitializeComponent();
data = service.getData();
}
}
I added singletons to pass the service to the component:
builder.Services.AddSingleton<MyComponent>();
builder.Services.AddSingleton<MyService>();
I also tried this method:
builder.Services.AddSingleton(sp => new MyComponent(sp.GetService<MyService>()));
None of them worked. Only default constructor called
I use the component in a page like this:
<ContentPage ...
xmlns:components="clr-namespace:MyApp.Components">
<components:MyComponent/>
</ContentPage>
How do I pass a service to a component?
You can also use dependency injection like in there.
Create a ServiceProvider class :
public static class ServiceProvider
{
public static TService GetService<TService>()
=> Current.GetService<TService>();
public static IServiceProvider Current
=>
#if WINDOWS10_0_17763_0_OR_GREATER
MauiWinUIApplication.Current.Services;
#elif ANDROID
MauiApplication.Current.Services;
#elif IOS || MACCATALYST
MauiUIApplicationDelegate.Current.Services;
#else
null;
#endif
}
Then you can simply use it in any component constructor :
_Contexte = ServiceHelper.GetService<Contexte>();
As for why you can't pass parameter to XAML components it's because they are converted to C# code and then automatically process. If the constructor has argument the mechanism handling the conversion wont know which instance to pass to the constructor (unless microsoft decide to handle DI, which may happend at some point).
You could not directly use dependency injection in a custom control.
See this issue on Github: Dependency Injection for Custom Controls. It's still enhancement under consideration and you could follow this issue.
There should be some workarounds. Just as tripjump comment in above issue, you could attach a bindable property and inject the viewModel from MainPage through this property. I just made a small demo for you.
For MyComponent control:
public partial class MyComponent : ContentView
{
public static readonly BindableProperty ServiceProperty = BindableProperty.Create(nameof(Service),typeof(MyService), typeof(MyComponent),propertyChanged: OnServiceChanged);
static void OnServiceChanged(BindableObject bindable, object oldValue, object newValue)
{
// Property changed implementation goes here
MyService a = newValue as MyService;
}
public MyService Service
{
get => (MyService)GetValue(MyComponent.ServiceProperty);
set => SetValue(MyComponent.ServiceProperty, value);
}
public MyComponent()
{
InitializeComponent();
}
}
For MainPage which consumes MyComponent:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
...
x:Name="this">
<components:MyComponent BindingContext="{x:Reference this}" MyService="{Binding BindingContext.service}"/>
For MainPageViewModel which is BindingContext of MainPage:
public class MainPageViewModel
{
public MyService service { get; set; }
public MainPageViewModel(MyService myService)
{
service = myService;
}
}
And also add service in MauiProgram:
builder.Services.AddSingleton<MainPage>();
builder.Services.AddSingleton<MainPageViewModel>();
builder.Services.AddSingleton<MyService>();
Hope it works for you.
I am having to re-write a large WinForms application and I want to use MVC to allow increased testing capability etc. I want to also adopt Ninject as my IoC container as it is lightweight, fast and will increase the exstensibility of my application going forward.
I have done a great deal of reading and I have managed to make a start on the arcitecture of this new application. However, I am not sure i have the right idea when using Ninject. The code...
Starting with Program.cs and related classes...
static class Program
{
[STAThread]
static void Main()
{
FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath());
Log.LogHandler = fileLogHandler;
Log.Trace("Program.Main(): Logging initialized");
CompositionRoot.Initialize(new ApplicationModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
}
}
public class CompositionRoot
{
private static IKernel _ninjectKernel;
public static void Initialize(INinjectModule module)
{
_ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return _ninjectKernel.Get<T>();
}
}
public class ApplicationModule : NinjectModule
{
public override void Load()
{
Bind(typeof(IApplicationShellView)).To(typeof(ApplicationShellView));
}
}
An my ApplicationShellView is
public partial class ApplicationShellView : Form, IApplicationShellView
{
public ApplicationShellView()
{
InitializeComponent();
}
public void InitializeView()
{
dockPanel.Theme = vS2012LightTheme;
}
}
with interface
public interface IApplicationShellView
{
void InitializeView();
}
The controller for this view is
public class ApplicationShellController
{
private IApplicationShellView view;
public ApplicationShellController(IApplicationShellView view)
{
view.InitializeView();
}
}
Currently the controller is redundant, and although this code works and my view displays, I have some important questions...
Should I be using the ApplicationShellController to initialize my form, currently this is not using MVC "pattern"?
It feels like I have written a Service Locator, and from what I have read, this is bad. How else should I be using Ninject for IoC to initialize my application?
Any other advice as to what I am doing right[if anything!]/wrong?
Thanks very much for your time.
No you should not be initializing your controller, this exactly what IoC and Ninject are for. When you initialize your view/form, Ninject should make the view fetch the controller it depends on, which will auto fetch controllers it depends on and so on.
Of course this won't work like you've set it up right now. For starters, your view needs to know the controller it depends on.
public partial class ApplicationShellView : Form, IApplicationShellView
{
private IApplicationShellController _controller;
public ApplicationShellView()
{
InitializeComponent();
init();
//InitializeView()
}
private void init() {
_controller = NinjectProgram.Kernel.Get<IApplicationShellController>();
//Because your view knows the controller you can always pass himself as parameter or even use setter to inject
//For example: _controller.SetView1(this);
}
public void InitializeView()
{
dockPanel.Theme = vS2012LightTheme;
}
}
public class ApplicationShellController : IApplicationShellController
{
//Implementes functionality for the MainForm.
public ApplicationShellController()
{
//Also possible to add other controllers with DI
}
}
This does indeed look like a Service Locator, simply initializing your view should do be sufficient.
public class NinjectProgram
{
//Gets the inject kernal for the program.
public static IKernel Kernel { get; protected set; }
}
public class Program : NinjectProgram
{
[STAThread]
private static void Main()
{
Kernel = new StandardKernel();
Kernel.Load(new ApplicationModule());
Application.Run(new ApplicationShellView());
}
}
public class ApplicationModule : NinjectModule
{
public override void Load()
{
//Here is where we define what implementations map to what interfaces.
Bind<IApplicationShellController>().To<ApplicationShellController>();
//We can also load other modules this project depends on.
Kernel.Load(new NinjectModule());
}
}
Don't try and make it too complicated, a good start is important but you can always apply changes when and where needed during development.
I believe the following GitHub project might be a good starting point: Example of how you might use Ninject within a WinForms application.
If you have any more questions, just leave a comment and I'll try to answer them as soon as possible
I am building a WPF browser application with MVVM pattern.
I have a first page (ConsultInvoice) with a dataGrid. When I double click on one of the row I want to navigate to another page (EditInvoice) passing the selected row in argument to my constructor.
I know if I want do things properly I should use a dependency injection, but I don't really see how to use it here.
How can I simply pass this constructor?
ConsultInvoiceViewModel
private Invoice _selected;
public Invoice Selected
{
get
{
return _selected;
}
set
{
_selected = value;
OnPropertyChanged("Selected");
}
}
private void Edit()
{
EditInvoiceViewModel editInvoice = new EditInvoiceViewModel(Selected);
/* doing something here*/
}
public ICommand EditCommand
{
get
{
return editCommand ?? (editCommand = new RelayCommand(p => this.Edit(), p => this.CanEdit()));
}
}
EditInvoiceViewModel
public class EditInvoiceViewModel : ViewModelBase
{
public Context ctx = new Context();
Invoice invoice;
PreInvoice preInvoice;
#region properties
private ObservableCollection<PreInvoice> collection;
public ObservableCollection<PreInvoice> Collection
{
get
{
return collection;
}
set
{
collection = value;
OnPropertyChanged("Collection");
}
}
#endregion
public EditInvoiceViewModel(Invoice inv)
{
/* do stuff*/
}
}
Basically you should avoid passing such parameters into the ViewModels constructor, as wiring it with Inversion of Control/Dependency Injection becomes a pain. While you can use Abstract Factory pattern to resolve objects with runtime parameters, it's imho not suitable for ViewModels.
Instead I always suggest using a form of navigation pattern, similar to how Microsoft's Patterns & Practices team has done with Prism. There you have an INavigationAware interface which your ViewModels can implement. It has 2 methods, NavigateTo and NavigateFrom.
And there is a navigation service. The navigation service will switch the views and before switching calling NavigateFrom in the current ViewModel (if it implements it. One can use it to check if data is saved and if necessary cancel the navigation. After the new View has been loaded and the ViewModel assigned to it, call NavigateTo in the newly navigated ViewModel.
Here you'd pass the parameters required for the ViewModel, in your case invoiceId. Try avoid passing whole models or complex objects. Use the invoiceid to fetch the invoice data and to populate your editing ViewModel.
A basinc implementation from my former answer (can be found here):
public interface INavigationService
{
// T is whatever your base ViewModel class is called
void NavigateTo<T>() where T ViewModel;
void NavigateToNewWindow<T>();
void NavigateToNewWindow<T>(object parameter);
void NavigateTo<T>(object parameter);
}
public class NavigationService : INavigationService
{
private IUnityContainer container;
public NavigationService(IUnityContainer container)
{
this.container = container;
}
public void NavigateToWindow<T>(object parameter) where T : IView
{
// configure your IoC container to resolve a View for a given ViewModel
// i.e. container.Register<IPlotView, PlotWindow>(); in your
// composition root
IView view = container.Resolve<T>();
Window window = view as Window;
if(window!=null)
window.Show();
INavigationAware nav = view as INavigationAware;
if(nav!= null)
nav.NavigatedTo(parameter);
}
}
// IPlotView is an empty interface, only used to be able to resolve
// the PlotWindow w/o needing to reference to it's concrete implementation as
// calling navigationService.NavigateToWindow<PlotWindow>(userId); would violate
// MVVM pattern, where navigationService.NavigateToWindow<IPlotWindow>(userId); doesn't. There are also other ways involving strings or naming
// convention, but this is out of scope for this answer. IView would
// just implement "object DataContext { get; set; }" property, which is already
// implemented Control objects
public class PlotWindow : Window, IView, IPlotView
{
}
public class PlotViewModel : ViewModel, INotifyPropertyChanged, INavigationAware
{
private int plotId;
public void NavigatedTo(object parameter) where T : IView
{
if(!parameter is int)
return; // Wrong parameter type passed
this.plotId = (int)parameter;
Task.Start( () => {
// load the data
PlotData = LoadPlot(plotId);
});
}
private Plot plotData;
public Plot PlotData {
get { return plotData; }
set
{
if(plotData != value)
{
plotData = value;
OnPropertyChanged("PlotData");
}
}
}
}
An example of the INavigationAware interface used in Prism can be found on the projects github repository.
This makes it easy to pass parameter and async load your data (where there isn't any clean way to do this via constructor, as you can't await an async operation inside the constructor without locking, and doing this kind of things in the constructor is very discouraged).
I'm using ninject's kernel as a viewmodel locator in a WPF application.
The kernel helper class:
public static class IoCKernel
{
private static IKernel kernel;
public static void Init(params NinjectModule[] modules)
{
if (kernel == null)
{
kernel = new StandardKernel(modules);
}
}
public static T Get<T>()
{
return kernel.Get<T>();
}
}
And the ViewModelLocator exposes the Get method like:
public class ViewModelLocator : IViewModelLocator
{
public MainWindowViewModel MainWindowViewModel
{
get
{
return IoCKernel.Get<MainWindowViewModel>();
}
}
}
And when the instance is needed, it's called like:
IoCKernel.Get<IViewModelLocator>().MainWindowViewModel;
However, the IoCKernel.Get<MainWindowViewModel>() will always return a new instance. Is there a way to make it only work with one instance?
If you set up a binding in Ninject, you can call the InSingletonScope method:
Bind<IYourInterface>().To<YourClass>().InSingletonScope();
In your case (you do not have an interface for the view model) it might be:
Bind<MainWindowViewModel>().ToSelf().InSingletonScope();
See here for more info : Object Scopes in Ninject
I try to rewrite my Application using the MVVM pattern.
I have a window to show related documents for different objects with static methods like this:
public partial class ShowRelatedDocuments : Window
{
private ShowRelatedDocuments()
{
InitializeComponent();
}
public static void ShowRelatedDocument(A objA)
{
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.HandleA(objA);
srd.ShowDialog();
}
public static void ShowRelatedDocument(B objB)
{
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.HandleB(objB);
srd.ShowDialog();
}}
Is there a way to keep these methods static like this?
ShowRelatedDocumentsVM.ShowRelatedDocument(A objA);
ShowRelatedDocumentsVM.ShowRelatedDocument(B objB);
I didn't find anything about ViewModels and static methods. Can a VM create a instance of itself and show his View (here a window)?
Or is the better way to pass the objects as parameter to the constructor of the VM like this?
public ShowRelatedDocumentsVM(A objA)
{
HandleA(obj A)
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.DataContext = this;
srd.ShowDialog();
}
public ShowRelatedDocumentsVM(B objB)
{
HandleB(objB);
ShowRelatedDocuments srd = new ShowRelatedDocuments();
srd.DataContext = this;
srd.ShowDialog();
}
Or are both ways wrong, cause i breach the MVVM pattern due creating the view in the viewmodel?
Thx in advance.
How to display dialogs is one of the areas of MVVM that is not immediately clear, and there are a number of ways the behaviour can be achieved.
I would suggest using either a mediator (as described here) or by injecting a dependency on the view model that controls dialogs:
interface IDialogService
{
void ShowRelatedDocumentsA(A a);
}
...
class MyViewModel
{
private IDialogService _dialogService
public MyViewModel(IDialogService dialogService) { _dialogService = dialogService; }
public void DoSomething()
{
_dialogService.ShowDialog(...);
}
}
Either of these can will permit you to control the creation of the view outside of the view model, and will remove any explicit references from VM -> V.