I can see that the constructor of the ViewModelLocator is executed first when my application starts, but how to make the constructors of my ViewModels run after that, because inside them I have a registering that I want to happen in the beggining of my app. The constructor of my CustomViewModel runs when I enter the View because of the binding. The binding is to a master class called CompositeViewModel that contains my viewModels.
CompositeViewModel:
class CompositeViewModel
{
public static CustomViewModel customViewModel { get; set; }
static CompositeViewModel()
{
customViewModel = new CustomViewModel();
}
}
Here is my ViewModelLocator
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<CustomViewModel>();
SimpleIoc.Default.Register<MainViewModel>();
}
public CustomViewModelTripTypeView
{
get
{
return ServiceLocator.Current.GetInstance<CustomViewModel>();
}
}
This is the code in my CustomViewModels constructor:
public CustomViewModel()
{
Messenger.Default.Register<ObservableCollection<MyType>>
(
this,
(action) => ReceiveMessage(action)
);
}
private void ReceiveMessage(ObservableCollection<MyType> action)
{
DispatcherHelper.CheckBeginInvokeOnUI(() =>
{
this.MyDataSource.Clear();
foreach (MyTypet mt in action)
{
this.MyDataSource.Add(mt);
}
});
}
There is an overloaded method for Register method with bool argument
SimpleIOC.Default.Register<MainViewModel>(true);
This statement will immediately create an instance of MainViewModel.
Related
I have modular WPF app. In each module I want have its own Messenger. Because I want send some messages inside module (without tokens).
First module:
namespace NS1
{
public class MainViewModel : ViewModelBase
{
public MainViewModel(IMessenger messenger) : base(messenger)
{
.......
}
}
public class Module1
{
Messenger Messenger1;
static Module1()
{
Messenger1 = new Messenger();
SimpleIoc.Default.Register<IMessenger>(() => Messenger1);
SimpleIoc.Default.Register<MainViewModel>();
}
}
}
And second module:
namespace NS2
{
public class MainViewModel : ViewModelBase
{
public MainViewModel(IMessenger messenger) : base(messenger)
{
.......
}
}
public class Module2
{
Messenger Messenger2;
static Module2()
{
Messenger2 = new Messenger();
SimpleIoc.Default.Register<IMessenger>(() => Messenger2);
SimpleIoc.Default.Register<MainViewModel>();
}
}
}
Of course i have error in line
SimpleIoc.Default.Register<IMessenger>(() => Messenger2);
How I can inject Messenger1 to MainViewModel in NS1 and Messenger2 to MainViewModel in NS2?
Or is there other way? Tokens are good, but I must be sure that in different modules are no same tokens!
You need to register each separate instance of the Messenger interface with a key inside the SimpleIoc.
To get hold of the correct instance you'll also need to use the same key.
So constructor injecting will not be possible I'm afraid... you'll need to use the GetInstance method.
Code example is as follows
IMessenger m1 = new Messenger();
IMessenger m2 = new Messenger();
SimpleIoc.Default.Register<IMessenger>(() => m1, "Msg1");
SimpleIoc.Default.Register<IMessenger>(() => m2, "Msg2");
var t = SimpleIoc.Default.GetInstance<IMessenger>("Msg1");
var s = SimpleIoc.Default.GetInstance<IMessenger>("Msg2");
i want to inject a list to my viewmodel constructor with the ServiceLocator
my viewmodel:
public class ShowEmployeeViewModel: ViewModelBase
{
private IList<IEmployee> _empl;
public ShowEmployeeViewModel(IList<IEmployee> emp)
{
this._empl = emp;
_empl.Add(new Employee() { empName = "foo", enpFunction = "bar" });
}
}
my servicelocator:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
//register the interface against the class
SimpleIoc.Default.Register < IList < IEmployee >, List <Employee>>();
SimpleIoc.Default.Register<ShowEmployeeViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public ShowEmployeeViewModel ShowEmployee
{
get
{
return ServiceLocator.Current.GetInstance<ShowEmployeeViewModel>();
}
}
when i run this code i got an error:
"Cannot register: Multiple constructors found in List`1 but none marked with PreferredConstructor."
PS: i only got this error when i try to registre a list "IList" but when i register my interface like this:
SimpleIoc.Default.Register < IEmployee , Employee >();
it works fine, any idea how to register a list?
thanks in advance
Don't map the IList interface, use a factory for your ShowEmployeeViewModel class:
SimpleIoc.Default.Register(() => new ShowEmployeeViewModel(new List<IEmployee>()));
I have a ViewModel called MainViewModel (of course) which contains multiple Contructors as per the below:
[ImportingConstructor]
public MainViewModel(IWindowManager windowManager)
: this(windowManager, new DataProvider(), new LocalJsonPersistenceManager())
{
}
[PreferredConstructorAttribute]
public MainViewModel(IWindowManager windowManager, IInformationProvider infoProvider,
IPersistenceManager persistenceManager)
{
//generating data, handling clicks etc.
}
Inside that ViewModel is a public item that is constantly being updated (whenever a user clicks on a certain button and takes some actions on the form):
public Item ClickedItem
{
get { return clickedItem; }
set
{
clickedItem = value;
NotifyOfPropertyChange(() => ClickedItem);
if (ClickedItem != null)
{
FindNextItem();
}
}
}
Now i have a UserControl I am building that contains a ListView that I personnalised to make it a sticky headered listview (header moves up whenever the next header is reached blabla ...). because I can only do this via a GroupStyled ListView, I must build the data for the ListView in the C# code behind.
EDIT:
I am trying it using a ViewModelLocator as such:
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
if (ViewModelBase.IsInDesignModeStatic)
{
// Create design time view services and models
//SimpleIoc.Default.Register<IDataService, DesignDataService>();
}
else
{
// Create run time view services and models
//SimpleIoc.Default.Register<IDataService, DataService>();
}
SimpleIoc.Default.Register<MainViewModel>();
}
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
And I am calling up the data's specific value as such:
var vm1 = (new ViewModelLocator()).Main;
testtxt.Text = vm1.ClickedItem.Name;
But it keeps giving me an error message on runtime on the line:
return ServiceLocator.Current.GetInstance<MainViewModel>();
in the ViewModelLocator's block:
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
With the error message {"Type not found in cache: Caliburn.Micro.IWindowManager."} and an InnerException message of null.
It looks like MEF cannot construct the IWindowManager, which is a dependency of your ViewModel.
Try registering at least the default instance from Caliburn.
Update
taken straight from caliburn.Micro's MEF-Bootstrapper:
container = CompositionHost.Initialize(
new AggregateCatalog(
AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()
)
);
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(container);
container.Compose(batch);
You can check the sample code from Caliburn.Micro Github Samples.
https://github.com/Caliburn-Micro/Caliburn.Micro/blob/master/samples/Caliburn.Micro.HelloMef/Caliburn.Micro.HelloMef/MefBootstrapper.cs
You need to register your WindowManager in MEF.
I'm developing simple application in WPF with MVVM Light Toolkit. I have two views:
HomeView (default)
CustomersView
This is part of the MainViewModel class:
public MainViewModel()
{
CurrentViewModel = Bootstrapper.Instance.Container.Resolve<HomeViewModel>();
}
private void ExecuteShowCustomersCommand()
{
CurrentViewModel = Bootstrapper.Instance.Container.Resolve<CustomersViewModel>();
}
In CustomerViewModel I have property:
public ObservableCollection<Customers> Customers
{
get { return _customers; }
set
{
if (_customers == value) return;
_customers = value;
RaisePropertyChanged(CustomersPropertyName);
}
}
And my question is, when I should call the web service to get customers data? In CustomerViewModel constructor?
I would do it in the constructor of the viewmodel and use a IoC Container to get the instance.
Application start
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<MyViewModel>();
ViewModel
public MyViewModel(IDataService DataService)
{
Mydata = DataService.GetData(); // Edit: Could also be done in a property with lazy load
}
Locator
public MyViewModel MyVM
{
get
{
return SimpleIoc.Default.GetInstance<MyViewModel>();
}
}
I have ViewModel in my Silverlight project
public class MainViewModel : BaseNotify, IMainViewModel
{
[Dependency("AccountID")]
public Guid AccountID { get; set; }
...
public MainViewModel()
{
if (AccountID != Guid.Empty)
{
PreSelectedAccount = new Account() { ID = AccountID };
SingleAccountMode = true;
}
}
....
}
I'm using Unity this way:
public static class IoC
{
static IoC()
{
Current = new UnityContainer();
Current.RegisterType<IMainViewModel, MainViewModel>();
}
public static UnityContainer Current { get; set; }
}
public partial class App : Application
{
[Dependency]
public IMainViewModel ViewModel { get; set; }
private void Application_Startup(object sender, StartupEventArgs e)
{
Guid accountId = "1234-5678-1234-5678-1234";
IoC.Current.BuildUp(this);
}
}
After calling BuildUp method,I have instance of MainViewModel in the App.ViewModel, but how I can set up Unity to inject also some value for MainViewModel.AccountId property value during BuildUp?
You need to resolve/buildup with an override:
IoC.Current.BuildUp(this, new PropertyOverride("AccountID", accountId));
Your code looks like you are using the ServiceLocator anti-pattern. A pattern like constructor injection is most often preferable.
Another question: Why do you hook up your MainViewModel with your App class? Usually you would use it as the DataContext for your MainView.
And it is not common to inject primitive values like Guids using a DI container.
As #nemesv mention you might need to change your BuildUp method call, but doing so, I don't think you can achieve what you need.
The problem is first instance of the MainViewModel is created and then AccountId is passed into it, so your logic in the constructor is never true, e.g.
if(AccountID != Guid.Empty)
is never true. Alternativly you can add this logic to Setter of the AccountID property, something inline with this:
public Guid AccountId {
get { return _accountId; }
set {
_accountId = value;
OnAccountIdChanged();
}
}
protected virtual void OnAccountIdChanged() {
if(AccountId != Guid.Empty) {
//do your thing here
}
}