Prism 7.2 How to inject an instance into ViewModel another Module - c#

i'm a new in a development of WPF applications with PRISM and AKKA.NET frameworks.
In my code for Shell Window I made an instance of ActorSystem.
public partial class App
{
private ActorSystem appActorSystem;
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
appActorSystem = ActorSystem.Create(System.Reflection.Assembly.GetEntryAssembly()?.GetName().Name);
containerRegistry.RegisterInstance(appActorSystem);
}
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
moduleCatalog.AddModule<CommandBar.CommandBarModule>();
}
protected override void OnExit(ExitEventArgs e)
{
appActorSystem.Terminate();
appActorSystem.Dispose();
base.OnExit(e);
}
}
The instance will be registered in DI Container (Unity) after creation.
In my application i have also a module.
public class CommandBarModule : IModule
{
private IContainerProvider _containerProvider;
public void OnInitialized(IContainerProvider containerProvider)
{
_containerProvider = containerProvider;
var appActorSystem = _containerProvider.Resolve<ActorSystem>();
var regionManager = _containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion(regionNames.CommandBar, typeof(ViewA));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
I want to get the instance of the Actorsystem in the module. It works fine in CommandBarModule Class.
But I want also to get the instance of my Actorsystem in the ViewModel of the module...
public class ViewAViewModel : BindableBase
{
private string _message;
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
public ViewAViewModel()
{
Message = "test";
}
}
My First Idea was, that i can do injection of IContainerProvider into the constructor of the ViewModel, like this:
public class ViewAViewModel : BindableBase
{
private IContainerProvider _containerProvider;
private string _message;
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
public ViewAViewModel(IContainerProvider containerProvider)
{
_containerProvider = containerProvider;
var appActorSystem = _containerProvider.Resolve<ActorSystem>();
Message = "test";
}
}
But it doesn't work...
Could you please explain me how to do it right?

Inject the dependency, not the container:
public ViewAViewModel(ActorSystem appActorSystem)
{
Message = "test";
}
It does not matter in which module a service is registered and where it is resolved, as long as it's resolved after being registered.

Related

Trigger EventHandler of one ViewModel in another ViewModel

I have achieved desired result with MessagingCenter, but I have got an information from reading Xamarin articles that MessagingCenter is not the preferred way to trigger 30+ events. Additional to that I have to unsubscribe from MessagingCenter after action has been done. I want to have Settings page where I would have 30+ settings that have to be changed across whole application in different views. How I can inject SettingsViewModel into other ViewModels in Xamarin.Forms application?
SettingsViewModel.cs:
namespace MessagingCenterApp.ViewModels
{
public class SettingsViewModel : BaseViewModel, ISettingsViewModel
{
public ICommand ChangeCommand { get; set; }
public SettingsViewModel()
{
Title = "Settings";
this.BoxColor = Color.Red;
this.ChangeCommand = new Command(this.ChangeColor);
}
private void ChangeColor()
{
this.BoxColor = Color.FromHex(this.BoxColorS);
MessagingCenter.Send<Object, Color>(this, "boxColor", this.BoxColor);
}
private Color _boxColor;
public Color BoxColor
{
get => _boxColor;
set
{
_boxColor = value;
this.OnPropertyChanged();
}
}
private string _boxColorS;
public string BoxColorS
{
get => Preferences.Get("BoxColor", "#17805d");
set
{
Preferences.Set("BoxColor", value);
this.ChangeColor();
this.OnSettingsChanged();
this.OnPropertyChanged();
}
}
public event EventHandler<SettingsChangedEventArgs> SettingsChanged;
private void OnSettingsChanged() => this.SettingsChanged?.Invoke(this, new SettingsChangedEventArgs(this.Settings));
public Settings Settings { get; private set; }
}
}
HomeViewModel.cs:
namespace MessagingCenterApp.ViewModels
{
public class HomeViewModel : BaseViewModel
{
public HomeViewModel()
{
this.Title = "Home";
MessagingCenter.Subscribe<Object, Color>(this, "boxColor", (sender, arg) =>
{
System.Diagnostics.Debug.WriteLine("received color = " + arg);
this.BoxColor = arg;
});
this.BoxColor = Color.Red;
this.SettingsViewModel = new SettingsViewModel();
this.SettingsViewModel.SettingsChanged += OnSettingsChanged;
}
private void OnSettingsChanged(object sender, SettingsChangedEventArgs e)
{
throw new NotImplementedException();
}
private Color _boxColor;
public Color BoxColor
{
get => _boxColor;
set
{
_boxColor = value;
OnPropertyChanged();
}
}
private ISettingsViewModel SettingsViewModel { get; }
}
}
Should I somehow do all in MainViewModel? I mean:
namespace MessagingCenterApp.ViewModels
{
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
this.SettingsViewModel = new SettingsViewModel();
this.HomeViewModel = new HomeViewModel(this.SettingsViewModel);
}
public SettingsViewModel SettingsViewModel { get; set; }
public HomeViewModel HomeViewModel { get; }
}
}
Then initialized it in AppShell? I could not get this approach working.
Important! I don't want to use any MVVM framework! Only native behaviour.
mvvmcross' Messenger is alleged to be "lighter weight" than X-Form's built-in Messaging Center.
I use mvvmcross Messenger by defining some helper methods in a "BasePage". Then each page inherits from "BasePage" rather than "ContentPage".
This automatically handles "unsubscribe" of each method. And makes it easier to manage mvvmcross' "subscription tokens".
BasePage.xaml.cs:
// If not using mvvmcross, this could inherit from ContentPage instead.
public class BasePage : MvxContentPage
{
protected readonly IMvxMessenger Messenger;
public BasePage()
{
this.Messenger = Mvx.IoCProvider.Resolve<IMvxMessenger>();
}
protected override void OnAppearing()
{
base.OnAppearing();
// Examples of subscribing to messages. Your subclasses of BasePage can also do this.
this.Subscribe<MyMessage1>(OnMyMessage1);
this.SubscribeOnMainThread<MyMessage2>(OnMyMessage2);
}
protected override void OnDisappearing()
{
UnsubscribeAll();
base.OnDisappearing();
}
#region Messenger Subscriptions
protected List<MvxSubscriptionToken> _subscriptions = new List<MvxSubscriptionToken>();
/// <summary>
/// Create subscription and add to "_subscriptions".
/// Call this from subclass' OnAppearing, once per subscription.
/// Automatically unsubscribed in OnDisappearing.
/// </summary>
/// <param name="token"></param>
/// <param name="msgType"></param>
protected void Subscribe<T>(Action<T> onMessage) where T : MvxMessage
{
var token = this.Messenger.Subscribe<T>(onMessage);
// Hold token to avoid GC of the subscription.
_subscriptions.Add(token);
}
protected void SubscribeOnMainThread<T>(Action<T> onMessage) where T : MvxMessage
{
var token = this.Messenger.SubscribeOnMainThread<T>(onMessage);
// Hold token to avoid GC of the subscription.
_subscriptions.Add(token);
}
/// <summary>
/// OnDisappearing calls this.
/// </summary>
private void UnsubscribeAll()
{
if (_subscriptions.Count > 0)
{
foreach (MvxSubscriptionToken token in _subscriptions)
{
// Per "https://www.mvvmcross.com/documentation/plugins/messenger", this is sufficient to Unsubscribe:
// "Subscriptions can be cancelled at any time using the Unsubscribe method on the IMvxMessenger or by calling Dispose() on the subscription token."
token.Dispose();
}
_subscriptions.Clear();
}
}
#endregion
}
For view models, class would be "BaseViewModel", that your view models inherit from. Contents similar to above, but different method names for Appearing/Disappearing.
BaseViewModel.cs:
public class BaseViewModel : MvxViewModel
{
...
// mvvmcross' MvxViewModel provides these.
protected override void ViewAppearing()
{
...
}
protected override void ViewDisappearing()
{
...
}
... Messenger Subscriptions methods ...
}

How to inject MonoBehaviour class to non Monobehaviour in Unity3d with zenject?

I want to organize my code as well as I can, but I have some trouble with its organization. I try to use SOLID principles and make separate entities. I want to use MVVM to view (unity-weld as well), and DI container (zenject as well).
It's my first project and I'm trying to organize code.
My question is how to inject LoginViewModel into LoginController as static class using zenject container from GameInstaller class.
[Binding]
public class LoginViewModel : MonoBehaviour, INotifyPropertyChanged
{
private string _username = "";
private string _passsword = "";
private string _errorMessage = "";
private bool _autologin = false;
[Inject]
private LoginController _loginController;
[Binding]
public string Username {
get
{
return _username;
}
set
{
if (_username == value)
{
return; // No change.
}
_username = value;
Debug.Log($"SET username: {value}");
OnPropertyChanged("Username");
}
}
[Binding]
public string Password {
get
{
return _passsword;
}
set
{
if (_passsword == value)
{
return; // No change.
}
_passsword = value;
Debug.Log($"SET password: {value}");
OnPropertyChanged("Password");
}
}
[Binding]
public bool Autologin {
get
{
return _autologin;
}
set
{
if (_autologin == value)
{
return; // No change.
}
_autologin = value;
Debug.Log($"SET autologin: {value}");
OnPropertyChanged("Autologin");
}
}
[Binding]
public void LoginButtonClick()
{
Debug.Log("LoginButtonClick");
_loginController.Login(this);
//ErrorMessage = "blabla";
}
[Binding]
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
if (_errorMessage == value)
{
return; // No change.
}
_errorMessage = value;
Debug.Log($"SET errorMessage: {value}");
OnPropertyChanged("ErrorMessage");
}
}
/// <summary>
/// Event to raise when a property's value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class LoginController
{
private readonly ApiController _apiController;
[Inject]
private readonly LoginViewModel _loginViewModel;
public LoginController(ApiController apiController)
{
_apiController = apiController;
}
public void Login(LoginViewModel loginViewModel)
{
try
{
string userJson = _apiController.PostLogin(loginViewModel.Username);
_loginViewModel.ErrorMessage = "bla bla trololo";
Debug.Log(userJson);
}
catch (Exception ex)
{
throw ex;
}
}
}
public class GameInstaller : MonoInstaller
{
[Inject]
Settings _settings = null;
public override void InstallBindings()
{
InstallViewModels();
InstallServices();
InstallSignals();
InstallControllers();
}
private void InstallViewModels()
{
Container.Bind<LoginViewModel>().AsSingle();
}
private void InstallControllers()
{
Container.Bind<LoginController>().AsSingle().NonLazy();
Container.Bind<ApiController>().AsSingle().NonLazy();
}
private void InstallServices()
{
}
private void InstallSignals()
{
}
[Serializable]
public class Settings
{
}
}
Let's start from here: https://unitylist.com/p/ja3/Unity-MVVM
A ViewModel (or VM) is what holds the data that will be presented on a view. It contains all the properties that can be bound to view elements. All ViewModels inherit from INotifyPropertyChanged which alerts the system when data changes and a UI element needs to be updated.
Since the ViewModel is in it's essence a simple object and not a service, I argue that it does not need go be injected anywhere.
What you could do, is inject a Factory and get your ViewModel from it.
In your code you are trying to inject the controller into your ViewModel and not the other way round.

how to use Unity with constructor injection to update the status in parent view of viewmodel1 using the child view & and its viewmodel2 in wpf

This is my UnityResolver Class to create the instance of IUnityContainer
public sealed class UnityResolver
{
private static IUnityContainer _unityContainer;
private static volatile UnityResolver _unityresolverinstance;
private static object syncRoot = new Object();
public static IUnityContainer UnityContainerInitiation
{
get
{
if (_unityContainer == null)
{
if (_unityresolverinstance == null)
{
lock (syncRoot)
{
if (_unityresolverinstance == null)
_unityresolverinstance = new UnityResolver();
}
}
}
return UnityResolver._unityContainer;
}
}
public UnityResolver()
{
_unityContainer = new UnityContainer();
_unityContainer.RegisterType<MaintainRouteViewModel>();
}
}
Below is my Base View and Its ViewModelCode
public partial class MaintainRouteView : UserControl
{
public MaintainRouteViewModel maintainRouteViewModel = null;
IUnityContainer container;
public MaintainRouteView()
{
InitializeComponent();
container = UnityResolver.UnityContainerInitiation;
maintainRouteViewModel = container.Resolve<MaintainRouteViewModel>();
this.DataContext = maintainRouteViewModel;
}
///This button will navigate to the child view.
private void AddRoute_Click(object sender, RoutedEventArgs e)
{
pageAnimationControl.ShowPage(new AddNewRouteView());
}
}
Its ViewModel..
public class MaintainRouteViewModel : viewModelbase
{
private string _statusSuccessMessage = null;
private string _statusFailMessage =null;
private ObservableCollection<RouteDetailsModel> _routeDetailsCollection;
public ObservableCollection<RouteDetailsModel> routeDetailsCollection
{
get
{
return this._routeDetailsCollection;
}
set
{
this._routeDetailsCollection = value;
RaisePropertyChanged("routeDetailsCollection");
}
}
public string StatusSuccessMessage
{
get
{
return _statusSuccessMessage;
}
set
{
_statusSuccessMessage = value;
this.RaisePropertyChanged("StatusSuccessMessage");
}
}
public string StatusFailMessage
{
get { return _statusFailMessage; }
set
{
_statusFailMessage = value;
this.RaisePropertyChanged("StatusFailMessage");
}
}
public MaintainRouteViewModel()
{
///it will load some data to the Observablecollection
getAllCurrentRouteData();
}
}
Now Below is my Child View and its ViewModel....
public partial class AddNewRouteView : UserControl
{
public AddNewRouteView()
{
InitializeComponent();
IUnityContainer container = UnityResolver.UnityContainerInitiation;
this.DataContext = container.Resolve<AddNewRouteViewModel>();
}
}
Its ViewModel....
public class AddNewRouteViewModel : viewModelbase
{
private MaintainRouteViewModel maintainRouteViewModel;
public ICommand SaveCommand
{
get;
set;
}
[InjectionConstructor]
public AddNewRouteViewModel(MaintainRouteViewModel maintainRouteViewModel)
{
this.maintainRouteViewModel = maintainRouteViewModel;
SaveCommand = new DelegateCommand<object>((a) => ValidateNewRoute());
}
private void ValidateNewRoute()
{
bool flag = saveAndValidate();
if(flag)
{
updateRouteStatus();
}
}
public void updateRouteStatus()
{
maintainRouteViewModel.StatusSuccessMessage = "New Route successfully Added..";
}
}
}
Can Anyone Tell me how to use this way to get the same object of MaintainRouteViewModel in my Child VM Constructor So that i will show the Updated Status Message in my Base view MaintainRouteView???
*It will Work Fine If i replace my MaintainRouteView with below code :
this Is an another approach to use IOC .i previously using this in my project. it Works Fine for me but now i want to implement the same thing using Unity Container. Please Help.
public partial class MaintainRouteView : UserControl
{
public MaintainRouteViewModel maintainRouteViewModel = null;
public MaintainRouteView()
{
InitializeComponent();
maintainRouteViewModel = new MaintainRouteViewModel();
this.DataContext = maintainRouteViewModel;
}
private void AddRoute_Click(object sender, RoutedEventArgs e)
{
pageTransitionControl.ShowPage(
new AddNewRouteView
{
DataContext = new AddNewRouteViewModel(maintainRouteViewModel)
});
}
}
I am able to solve this issue using the LifeTime Management of Unity Container Register Types.
it will work fine if i tell the container to create a singleton instance of the MaintainRouteViewModel Class.
using :
container.RegisterType<MaintainRouteViewModel>(
new ContainerControlledLifetimeManager());
But it's just a workaround to get the expected result. i want to achieve it using a proper dependency injection without any singleton instance principle. Can anyone please help to provide the solution.

Caliburn Micro - Share data between ViewModels

Starting from the Caliburn Micro "Simple MDI" example. I would like to achieve the following:
I would like to share the reference of a class between the ViewModels. The shareme.count should be passed as reference to all the ViewModels. This would allow me to change it from within each ViewModel.
How can I achieve this close to Caliburn Micro convention?
ShellViewModel.cs
public class ShellViewModel : Conductor<IScreen>.Collection.OneActive {
SharedClass _shareme;
public SharedClass shareme {
get { return _shareme; }
set {
_shareme = value;
NotifyOfPropertyChange(() => shareme);
}
}
public ShellViewModel() {
shareme.count = 1;
}
public void OpenTab() {
ActivateItem(new TabViewModel {
DisplayName = "Tab " + shareme.count++
});
}
TabViewModel.cs
public class TabViewModel : Screen {}
AppBootstrapper.cs
public class AppBootstrapper : BootstrapperBase {
SimpleContainer container;
public AppBootstrapper() {
Initialize();
}
protected override void Configure() {
container = new SimpleContainer();
container.Singleton<IWindowManager, WindowManager>();
container.Singleton<IEventAggregator, EventAggregator>();
container.PerRequest<IShell, ShellViewModel>();
}
protected override object GetInstance(Type service, string key) {
var instance = container.GetInstance(service, key);
if (instance != null)
return instance;
throw new InvalidOperationException("Could not locate any instances.");
}
protected override IEnumerable<object> GetAllInstances(Type service) {
return container.GetAllInstances(service);
}
protected override void BuildUp(object instance) {
container.BuildUp(instance);
}
protected override void OnStartup(object sender, System.Windows.StartupEventArgs e)
{
DisplayRootViewFor<IShell>();
}
}
The will also serve as answer to your duplicate question.
EventAggregator isn't just for "events" you can message pass data to any or all viewmodels that are listening for message or event signature in question.
public class ViewModelA : Screen, IHandle<ShareMeMessageA>
{
private readonly IEventAggregator _events;
private int _sharemecount;
public class ViewModelA(IEventAggregator events){
_events = events;
_events.Subscribe(this);
}
//... other bits out for brevity
//-- EDIT --
public void SomeEventClick(){
_event.PublishOnUiThread(new ShareMeMessageB(){ ... etc ... });
}
protected override void Deactivated(bool close){
_events.Unsubscribe(this);
}
private void Handle(ShareMeMessageA msg)
{
if(msg != null)
sharemecount = msg.Count;
}
}
as this is just an example you don't have to pass the class object at all you can pass any type you want bool, int, float, etc..

Interface events pattern

public interface IConnector
{
void Connect();
event EventHandler<EventArgs> Received;
// and more
}
public class ConnectorA: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
public class ConnectorB: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
Now let's say I have a factory like this:
public interface IConnectorFactory
{
void Create(string type);
}
public class ConnectorFactory: IConnectorFactory
{
public void Create(string type)
{
switch(type)
{
case "A":
return new ConnectorA();
case "B":
return new ConnectorB();
}
}
}
I inject this factory via constructor to Manager class:
public class Manager
{
IConnectorFactory _factory;
IConnector _actualConnector;
string _type;
public string Type
{
get
{
return _type;
}
set
{
_type = value;
if (_actualConnector != null)
{
_actualConnector.Disconnect();
_actualConnector.Received -= ReceivedFunc;
}
_actualConnector = _factory.Create(Type);
_actualConnector.Received += ReceivedFunc;
}
}
public Manager(IConnectorFactory factory)
{
_factory = factory;
}
public void Connect()
{
_actualConnector.Connect();
}
public void DoSomethingElse()
{
_actualConnector.DoSomethingElse();
_actualConnector.Received += ReceivedFunc;
}
public void ReceivedFunc()
{
}
}
Type is property that is set external (for example binded to UI ComboBox). Problem is that every time it is changed I have to unsubscribe old event and subscribe to new event. Isn't there some pattern for subscribing to interface event?
When you assign a value to _actualConnector:
_actualConnector = _factory.Create(Type);
your're just changing a field and the old object has still its Received event handled by ReceivedFunc. This has nothing to do with interfaces, this is the way objects and references work in .NET

Categories