WPF C# Ninject with a mainViewModel and multiple viewmodel problem - c#

I'm having problem with with implementing ninject in my application.
My application contains of a MainView view and viewmodel.
The MainView is using resource.xaml to draw the GUI. I'm also binding the viewmodel for each resource in my MainView.xaml.
I'm having a problem with implementing Ninject.
In my MainViewModel I'm creating;
public logViewModel ChangelogViewModel { get; set; } = new logViewModel();
public TabViewModel ToolTabViewModel { get; set; } = new ToolTabViewModel();
which I send as viewmodel content for the resource files.
How can I use ninject to automatically creating those additional viewmodels?
I'm also having problem with using the kernel to bind interfaces.
When my constructor launch, I don't get the functions from the ninject kernel binding.
It seem like at runtime when I check constructor, it only get my connectionstring which is a private variable in the viewmodel. But non of my functions is there.
Bind<IDataAccessor>().To<DataAccessor>().InSingletonScope().WithConstructorArgument("connectionString", connectionString);
Another weird thing is that it doesn't seem like my accessor is running its function when it's called. Does partial class have something to do with this?
I'm working with telerik components.

Like most DI containers, Ninject works with constructor injection.
You tell it what to create when a class/viewmodel constructor asks for a specific interface.
When you Get an instance of that class it provides the parameters you registered.
A quick and dirty example for flavour.
Instead of just a starturl I'm calling startup to show mainwindow.
private void Application_Startup(object sender, StartupEventArgs e)
{
var nj = new StandardKernel();
nj.Bind<IVMOne>().To<VMOne>().InTransientScope();
MainWindow mw = new MainWindow();
mw.DataContext = nj.Get<MainWindowViewModel>();
mw.Show();
}
Here I say when I ask for a IVMOne then give me a VMONe.
MainWindowViewModel wants a IVMOne for it's ctor.
So when I Get a mainwindowviewmodel ninject also gives me a VMOne.
class MainWindowViewModel
{
public IVMOne VMOne { get; set; }
public MainWindowViewModel(IVMOne vmOne)
{
VMOne = vmOne;
}
}
Which I can then bind to:
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock Text="{Binding VMOne.Hello}"/>
</Grid>

Related

How to properly notify view layer to open a dialog?

I have a WPF application and i'm trying to respect the MVVM pattern rules. One of my views contains button:
<Button
Command="{Binding BrowseCommand}"
Margin="50, 0, 0, 0"
Style="{StaticResource CommonButtonStyle}"
Width="100"
Height="30">
<TextBlock
Text="Browse"/>
</Button>
Button command calls a method:
private void Browse(object sender)
{
DialogService.BrowseForDestinationPath(DestinationPath);
}
The main purpose of this method is to show "Select-directory-dialog", collect data and return it to the view model.
public static class DialogService
{
public static event Action<string> FolderBrowseRequested;
...
public static void BrowseForDestinationPath(string initialPath)
{
FolderBrowseRequested?.Invoke(initialPath);
}
}
Event defined in my DialogService class is invoked, and the subscriber method located in code-behind of the dialog fires:
protected void OnFolderBrowseRequested(string initialPath)
{
string destinationPath = initialPath;
var browsingDialog = new VistaFolderBrowserDialog();
if(browsingDialog.ShowDialog(this).GetValueOrDefault())
{
destinationPath = browsingDialog.SelectedPath;
var dataContext = DataContext as UnpackArchiveWindowViewModel;
if (dataContext != null)
dataContext.DestinationPath = destinationPath;
}
DialogService.FolderBrowseRequested -= OnFolderBrowseRequested; //so dumb
}
The problem is i really don't like this solution, I'm convinced it's unnecessarily complicated and inelegant. How to properly show a dialog on button click, collect some data and deliver it to our view model? I would like to keep View and ViewModel seperated and fully respect MVVM regime.
You could first start by describing the behavior your DialogService needs in an interface.
public interface IDialogService
{
void BrowseForDestinationPath(string initialPath);
event PathSelectedEvent PathSelected;
}
public delegate void PathSelectedEvent(string destinationPath);
Your ViewModel would contain a member of type IDialogService and subscribe to the PathSelectedEvent. The BrowseForDestinationPath method would be called using your Browse method which is called using the Command.
You could then create a user control which implements IDialogService. You could either inject this through your ViewModels constructor or if your ViewModel had a property like
public IDialogService FolderBorwser {get;set;}
the benefit of this approach is that all your view model knows about is an interface. You now delegate the responsibility of creating a concrete instance to something else. I would reccomend using an Injection Container like Unity or MEF as they handle the job of managing and resolving dependencies.
I encourage you to write your own logic because it helps you to understand the problem of opening dialogs in MVVM, but if you hit a brick wall or wan't to take the easy way out, there is a library called MVVM Dialogs that can help you with these problems.
Using this library you would write your code like this.
private void Browse(object sender)
{
var settings = new FolderBrowserDialogSettings
{
Description = "This is a description"
};
bool? success = dialogService.ShowFolderBrowserDialog(this, settings);
if (success == true)
{
// Do something with 'settings.SelectedPath'
}
}

How to create view-model of prism when needed?

I really made a search for this topic and did not find anything, and because of that, I am asking the question here.
I have a WPF application with Prism installed.
I have wired the view-model with the view automatically by name convention
<UserControl x:Class="Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
and the model in the 'Model' like this
public class ViewAViewModel {
public ViewAViewModel () {
// time-resource consuming operations
}
}
the automatic binding work perfectly without a problem and the view and its corresponding view-model is matching, but the problem here.
I have a lot of those views say (50) and for every one of them, the view-model will be created with constructor exhausting the processes. This will make the startup of the application longer and also it will create a lot of view-models objects and put them in the RAM without being sure that they will be used at all.
What I need is to create the view-model class when the view is activated (I mean when the view is navigated to). Is this possible and if yes how?
Update
here is how I register the view with the Module, this is causing all the views to be created when the startup of the module.
public class Module1 : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("region1", typeof(View1));
regionManager.RegisterViewWithRegion("region1", typeof(View2));
is there any way to delay the creating of the views, until the navigation request come?
You could use navigation, for each view.
Or you must create an interfaces for your view and view model.
An example:
public interface IMyView
{
IMyViewModel ViewModel { get; set; }
}
public interface IMyViewModel
{
}
In the module or app.cs, in the method RegisterTypes you should register these.
containerRegistry.Register<IMyView, MyView>();
containerRegistry.Register<IMyViewModel, MyViewModel>();
You must implement IMyView interface in your MyView.cs class.
public partial class MyView : UserControl, IMyView
{
public MyView(IMyViewModel viewModel)
{
InitializeComponent();
ViewModel = viewModel;
}
public IMyViewModel ViewModel
{
get => DataContext as IMyViewModel;
set => DataContext = value;
}
}
After you could use it:
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
var firstView = containerProvider.Resolve<IMyView>();
regionManager.AddToRegion(RegionNames.MainRegion, firstView);
}
In such case you shouldn't use ViewModelLocator.AutoWireViewModel in your view.

using autofac in MVVM application

My application is going on a break mode after hitting a user button that loads the app setup. I have registered the component in the bootstrapper class.
How can I register the constructor of the user controller in bootstrap class so as to avoid the break?
public class Bootstrapper
{
public IContainer Bootstrap()
{
var builder = new ContainerBuilder();
builder.RegisterType<LoginView>().AsSelf();
builder.RegisterType<SchoolSectionDataService>().As<ISchoolSectionDataService>();
builder.RegisterType<AdminView>().AsSelf();
builder.RegisterType<School>().AsSelf();
builder.RegisterType<MainSchoolSetupViewModel>().AsSelf();
return builder.Build();
}
}
and the user control is:
private MainSchoolSetupViewModel _viewModel;
public School(MainSchoolSetupViewModel schoolSetupViewModel)
{
InitializeComponent();
_viewModel = schoolSetupViewModel;
DataContext = _viewModel;
Loaded += UserControl_Loaded;
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
_viewModel.Load();
}
Unfortunately passing viewmodel into user control's constructor is not possible but there few ways around it. The main thing usually is when building combining DI and XAML and MVVM is that only the view models are registered into the container.
Couple options are mentioned in the comments:
Add a static IContainer property in your Bootstrap. Call it in you user control's constructor to get the VM:
public School()
{
InitializeComponent();
_viewModel = Bootstrap.Container.Resolve<MainSchoolSetupViewModel>();
...
Skip DI and instead create the viewmodel instance in XAML:
<UserControl.DataContext>
<local:SchoolViewModel/>
</UserControl.DataContext>
But it's quite likely that you want to there's other possibilities:
Use ViewModelLocator to help out with DI. This is well documented in this answer: https://stackoverflow.com/a/25524753/66988
The main idea is that you create a new ViewModelLocator class:
class ViewModelLocator
{
public SchoolViewModel SchoolViewModel
{
get { return Bootstrap.Container.Resolve<SchoolViewModel>(); }
}
}
And create a static instance of it in App.xaml and use it to create the data context of your user control:
DataContext="{Binding SchoolViewModel, Source={StaticResource ViewModelLocator}}">
For other solutions, one option is to check out source code of some of MVVM Frameworks, like Caliburn.Micro. From Caliburn.Micro you can find ViewModelLocator and ViewLocator which might interest you.

What is the MVVM way to call WPF command from another viewmodel?

I'm trying to learn more about MVVM implementation in WPF and currently need some guidance on navigation using ViewModels. I'm following WPF navigation example from Rachel's blog and need a way to call Command of ApplicationViewModel from other ViewModel.
As per the blog, switching views from MainWindow is pretty clear, but I want to know more about inter-view navigation i.e. say I've Home, Product and Contact button on MainWindow along with View and ViewModel classes, now I want to open Contact page from some button inside Home view instead of MainWindow. I have written some code in Home ViewModel to achieve the same but I doubt whether this is the best practice of MVVM. And is there any way to achieve the same from HomeView.XAML?
Code Snippet from blog - ApplicationViewModel.cs
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private List<IPageViewModel> _pageViewModels;
public ApplicationViewModel()
{
// Add available pages in c'tor
PageViewModels.Add(new HomeViewModel(this));
PageViewModels.Add(new ProductsViewModel());
PageViewModels.Add(new ContactViewModel());
}
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p), p => p is IPageViewModel);
return _changePageCommand;
}
}
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}
Code Snippet from blog - ApplicationView.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type local:HomeViewModel}">
<local:HomeView />
</DataTemplate>
<!-- Data template for other views -->
</Window.Resources>
<DockPanel>
<Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
<ItemsControl ItemsSource="{Binding PageViewModels}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.ChangePageCommand,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding }"/>
<!--All closing tags-->
My code inside HomeViewModel.cs
// This is the command to get bind with my button inside Home view to invoke Contact view
private ICommand _loadContactCommand;
public ICommand LoadContactCommand
{
get
{
if (_loadContactCommand == null)
_loadContactCommand = new RelayCommand(p => LoadOtherView());
return _loadContactCommand;
}
}
private void LoadOtherView()
{
// _appVM is the instance of 'ApplicationViewModel' which is being set from c'tor
// Even I'm thinking to pass Contact view member of ApplicationViewModel class here,
// as I need exactly the same instance of the Contact which has been created earlier
_appVM.ChangePageCommand.Execute(new ContactViewModel());
}
There's a couple of ways I'd do this.
The first, if the action is a service type of interaction, which I think this is a reasonably good example of, I would describe the action in an interface and inject it as a dependency into the ViewModels that need it.
This is effectively what you are doing, but it's worth abstracting it out into an interface. This provides less tight coupling between the two ViewModels.
Here is an example of wrapping up the functionality in an IPageDisplay interface:
public interface IPageDisplay
{
IPageViewModel GetCurrentPage();
void ChangeViewModel(IPageViewModel newPage);
}
Your ApplicationViewModel implements it and has the exact same methods it did before:
public class ApplicationViewModel: IPageDisplay
{
// implement like you are doing
You're HomeViewModel then takes as an interface, not the 'whole' ViewModel:
class HomeViewModel
{
HomeViewModel(IPageDisplay pageDisplay) {//constructor stuff}
private void LoadOtherView()
{
// Instead of interacting with a whole ViewModel, we just use the interface
_pageDisplay.ChangePageCommand.Execute(new ContactViewModel());
}
This is 'safer' as it's more abstract. You can test HomeViewModel without creating a AppViewModel by just mocking the IPageDisplay. You can change how pages are displayed or the implementation of AppViewModel, you can also display your pages in any other kind of location, by having some other implementation of IPageDisplay.
It's worth noting that any page that needs to perform navigation actions will require an IPageDisplay. It can be troublesome matching up all these dependencies if you have many of them - that's where something like a Dependency Injection framework can really help out.
The second would be a mediator pattern as suggested in the comments. You could have a common mediator PageManager that defines the ChangeViewModel(IPageViewModel newPage); method and fires a ChangeViewModelRequest event or callback. The ApplicationViewModel, and any other ViewModels that want to change the current page accept the PageManager instance as a dependency. ApplicationViewModel listens to the event, the other's call ChangeViewModelRequest to trigger it.
Again, a Dependency Injection will need to be managed effectively if this is in a complex application.
This naturally leads onto the third. Which is a extension of the mediator pattern, an Event Aggregator.
An event aggregator is a generic service that allows all different ViewModels to raise, or subscribe to application wide events. It's definitely worth looking at.
Here, your ApplicationViewModel subscribes to the event:
public class ApplicationViewModel
{
private EventAgregator _eventAggregator;
ApplicationViewModel(EventAgregator eventAggregator)
{
this._eventAggregator = eventAggregator;
_eventAggregator.Subscribe('ChangeViewModelRequest', (EventArgs eventArgs) => ChangeViewModel(eventArgs.Parameter))
}
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
PageViewModels.Add(viewModel);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == viewModel);
}
}
And the HomeViewModel publishes to the event:
private void LoadOtherView()
{
_eventAggregator.Publish("ChangeViewModelRequest", new EventArgs(new ContactViewModel()));
}
There are plenty of Event Aggregators you can use, some built into MVVM frameworks like Prism.
While, like all the others, this is a dependency - it's a very generic one. Chances are, most of your ViewModels will need access to the aggregator instance and have it as a dependency, as it could be used for almost all inter-view-model communication. Simply having all VMs pass it to any created VMs in the constructor could work for a simple application. But I'd still say something that supports dependency injection (say, factory pattern?) would be worth implementing.
Edit:
Here's what you need for your HomeViewModel:
public class HomeViewModel : IPageViewModel // doesn't implement IPageDisplay
{
private IPageDisplay _pageDisplay;
public HomeViewModel(IPageDisplay pageDisplay)
{
// HomeViewModel doesn't implement IPageDisplay, it *consumes* one
// as a dependency (instead of the previous ApplicationViewModel).
// Note, that the instance you're passing still is the ApplicationViewModel,
// so not much has actually changed - but it means you can have another
// implementation of IPageDisplay. You're only linking the classes together
// by the functionality of displaying a page.
_pageDisplay= pageDisplay;
}
public string Name
{
get
{
return "Home Page";
}
}
private ICommand _loadDashboardCommand;
public ICommand LoadDashboardCommand
{
get
{
if (_loadDashboardCommand == null)
{
_loadDashboardCommand = new RelayCommand(
p => LoadOtherView());
}
return _loadDashboardCommand;
}
}
private void LoadOtherView()
{
// Here you have the context of ApplicatiomViewModel like you required
// but it can be replaced by any other implementation of IPageDisplay
// as you're only linking the little bit of interface, not the whole class
_pageDisplay.ChangeViewModel(new DashboardViewModel());
}
}
}

How to use StructureMap to inject service into a ViewModel referenced from XAML?

I may be going about this all wrong, but I thought I was on the right path. I'm building a WPF app for the first time and am still getting used to some of the ways things are done.
My Thinking
My goal is to make my ViewModel class more testable.
The ViewModel depends on a service class and wouldn't be functional without it.
Therefore, it seems to make sense to me to inject the service into the ViewModel so that I can also start mocking it for testing purposes.
What I did
Made a XAML reference to my view model, which is called "UploaderViewModel":
<Window.DataContext>
<viewModels:UploaderViewModel>
</viewModels:UploaderViewModel>
</Window.DataContext>
NOTE: This works fine before I try to do the injection
Set up structure map in my app.xaml.cs file:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
ProcessStartupArguments(eventArgs);
}
Which calls my bootstrapper class:
public static class ContainerBootstrapper
{
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(
x => x.For<IVEDocumentService>().Use<VEDocumentService>()
);
}
}
I then have my UploaderViewModel class take the IVEDocumentService as a Constructor parameter:
public UploaderViewModel(IVEDocumentService documentService)
{
_documentService = documentService;
//other unrelated code below this
}
What's Going Wrong
I'm receiving an XAML error that says there's no default constructor found for the ViewModel, which makes sense, since I have no UploaderViewModel(), only UploaderViewModel(IVEDocumentService documentService).
Question
What's the best way to resolve this?
Should I create an UploaderViewModel() constructor and make StructureMap call UploaderViewModel with an instance of the service, or is there a better way?
Update 1 -- Attempting to Use a Resource Dictionary, StructureMap error.
Based on this SO Answer to a similar question, I tried:
Updating my App_Startup to add an instance of the UploaderViewModel to a resources dictionary:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
var uploaderViewModel = new UploaderViewModel(new Container().GetInstance<IVEDocumentService>());
Current.Resources["UploaderViewModel"] = uploaderViewModel;
ProcessStartupArguments(eventArgs);
}
Added DataContext="{StaticResource UploaderViewModel}" to my <Window> declaration
Commented out my <viewModels:UploaderViewModel/> declaration
Upon doing this, StructureMap now throw an error:
"StructureMap Exception Code: 202No Default Instance defined for
PluginFamily
I figured it out. I think I wasn't configuring the static resource correctly. The solution, using StructureMap, was to do the following:
App.xaml.cs
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
//Other logic
}
ContainerBootstrapper Class
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(x => x.For<IVEDocumentService>().Use<VEDocumentService>());
}
MainWindow.xaml.cs
I moved the resource creation into this class:
public MainWindow()
{
var uploaderViewModel = new UploaderViewModel(ObjectFactory.GetInstance<IVEDocumentService>());
this.Resources["UploaderViewModel"] = uploaderViewModel;
InitializeComponent();
}
MainWindow.xaml
On the <Window> definition, I set DataContext="{StaticResource UploaderViewModel}".
Works like a charm. I believe it was a combination of not binding the resources at the proper scope (window).

Categories