Caliburn.Micro WPF: IoC.Get Returns Null - c#

My code looks like this:
Bootstrapper.cs
public class Bootstrapper : BootstrapperBase
{
private SimpleContainer _container = new SimpleContainer();
public Bootstrapper()
{
Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
base.OnStartup(sender, e);
DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
_container.RegisterPerRequest(typeof(ShellViewModel), null, typeof(ShellViewModel));
}
protected override object GetInstance(Type service, string key)
{
return _container.GetInstance(service, key);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return _container.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}
And my ShellViewModel looks like this:
ShellViewModel.cs
public class ShellViewModel : Conductor<Screen>
{
public ShellViewModel
{
var aViewModel = IoC.Get<AViewModel>();
ActivateItem(aViewModel);
}
}
But whenever I run the program, a blank screen is shown. When I debug it, it said that the aViewModel is null.
Is there anything wrong with the Bootstrapper?

Based on the code provided, AViewModel is not registered with the container in the Bootstrapper so IoC does not know it exists, thus it will return null when requested to Get that type
For example
_container.RegisterPerRequest(typeof(AViewModel), null, typeof(AViewModel));
All types that need to be resolved by IoC should first be registered with the backing container.

Related

Prism RequestNavigate from PrismApplication immediately on start

In Prism 7 I can RegisterForNavigation and RequestNavigate from IModule like this:
public class ModuleAModule : IModule
{
public void OnInitialized(IContainerProvider containerProvider)
{
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RequestNavigate("ContentRegion", "PersonList");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<PersonList>();
}
}
and I know that I can RegisterForNavigation from PrismApplication like this:
public partial class App : PrismApplication
{
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<ViewA>();
}
}
but how can I RequestNavigate from PrismApplication immediately on start?
I have tried this:
public class MainWindowViewModel : BindableBase
{
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
but this.regions.Count is 0 in RegionManager from Prism
private IRegion GetRegionByName(string regionName)
{
return this.regions.FirstOrDefault(r => r.Name == regionName);
}
"ContentRegion" definitely exists, because it works if I try from the IModule and I know that RegisterTypes from PrismApplication executes before the MainWindowViewModel constructor.
I don't know what I am missing and I can't find the answer in any examples or tutorials.
Thank you for your help!
Your best bet is to override OnInitialized in your application and do the navigation there. You can access the container to fetch the region manager through the Container property.
If you use a bootstrapper, you can override InitializeModules and navigate there.

How to set bindings for Start/StopTrackingTouch for Seekbar using MvvmCross

I got sort of a typical music player window, music plays and seekbar point is moving while it plays.
I've done it using default mvvmcross binding to the property (which is changed through the EventHandler binding) like here:
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/playprogress"
local:MvxBind="Progress ValueOfTimer"
/>
So now I want the user to be able to move it forward and back.
I've been trying to bind it like this:
public class PlayWindowView : MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
var set = this.CreateBindingSet<PlayWindowView, PlayWindowViewModel>();
SeekBar seek = FindViewById<SeekBar>(Resource.Id.playprogress);
set.Bind(seek).For("Max").To(viewModel => viewModel.MaxTimerValue);
set.Bind(seek).For("StopTrackingTouch").To(viewModel => viewModel.PlayProgressChanged);
set.Apply();
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.playwindow_view);
}
}
Viewmodel part looks like this:
public class PlayWindowViewModel : MvxViewModel<ListMenuItemDto>
{
private long _valueOfTimer;
public long ValueOfTimer
{
get { return _valueOfTimer; }
set
{
_valueOfTimer = value;
RaisePropertyChanged(() => ValueOfTimer);
}
}
//...
public MvxAsyncCommand<long> PlayProgressChanged
{
get { return new MvxAsyncCommand<long>(OnPlayProgressChange);}
}
private async Task OnPlayProgressChange(long progr)
{
await _playingService.SetTime((int) progr).ConfigureAwait(false);
}
}
But looks like it's not working.
I mean, it's not even getting into OnPlayProgressChange. But on view is appearing it goes into command PlayProgressChanged one time.
How can I bind this event (and such kind of events like StartTrackingTouch, StopTrackingTouch) to the function correctly?
P.S.
just FYI I using MvvmCross 5
UPD 28.11.2017
Tried custom binding and even Progress binding stoped working now.
So, xaml looks like this now:
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/playprogress"
local:MvxBind="Progress ValueOfTimer, OnStopTrackingTouch PlayProgressChanged"
/>
And binder code is here
public class SeekbarStopTrackingTouchEventBinding: MvxAndroidTargetBinding
{
private readonly SeekBar _seekbar;
private IMvxAsyncCommand _command;
public SeekbarStopTrackingTouchEventBinding(SeekBar seekbar) : base(seekbar)
{
_seekbar = seekbar;
_seekbar.StopTrackingTouch += ViewOnStopTrackingTouch;
}
private void ViewOnStopTrackingTouch(object sender, SeekBar.StopTrackingTouchEventArgs e)
{
if (_command != null)
{
_command.Execute(e);
}
}
public override Type TargetType
{
get { return typeof (IMvxAsyncCommand); }
}
protected override void SetValueImpl(object target, object value)
{
_command = (IMvxAsyncCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_seekbar.StopTrackingTouch -= ViewOnStopTrackingTouch;
}
base.Dispose(isDisposing);
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
}
In Setup:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<SeekBar>("OnStopTrackingTouch", (seekbar) => new SeekbarStopTrackingTouchEventBinding(seekbar)));
}
UPD2
Changed binding like this: local:MvxBind="Progress ValueOfTimer; OnStopTrackingTouch PlayProgressChanged" (notice ; here) and event fires now, yes!
But now the thing is - in binder _command is stays null even after SetValueImpl fired and _command = (IMvxAsyncCommand)value; is fine, value points to setted ViewModel property.
How come?
UPD3
Found out it can't cast object to IMvxAsyncCommand.
I fixed that by changing
IMvxAsyncCommand _command to IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> _command;
Will sum everything up in the answer.
But now I got the question - whats best practice in this case?
So, I don't know if it's a good way, but for now its working and I'm kinda happy with it.
Hope this would help somebody like me.
Custom Bindings approach is the key here. Really useful stuff is here:
In MvvmCross how do I do custom bind properties
MvvmCross Custom Event Binding Event Args
MVVMCross Bindings in Android
So, in my case, to make app listen to SeekBar OnStopTrackingTouch event I done this:
Created binding class:
public class SeekbarStopTrackingTouchEventBinding: MvxAndroidTargetBinding
{
private readonly SeekBar _seekbar;
private IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> _command;
private string testString;
public SeekbarStopTrackingTouchEventBinding(SeekBar seekbar) : base(seekbar)
{
_seekbar = seekbar;
_seekbar.StopTrackingTouch += ViewOnStopTrackingTouch;
}
private void ViewOnStopTrackingTouch(object sender, SeekBar.StopTrackingTouchEventArgs e)
{
if (_command != null)
{
_command.Execute(e);
}
}
public override Type TargetType
{
get { return typeof (IMvxAsyncCommand); }
}
protected override void SetValueImpl(object target, object value)
{
try
{
_command = (IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs>)value;
}
catch (Exception e)
{
Log.Error("SOME BINDER FAIL\n\t" + e.Message + "\n", "SOME BINDER FAIL\n\t" + e.Message + "\n");
throw;
}
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_seekbar.StopTrackingTouch -= ViewOnStopTrackingTouch;
}
base.Dispose(isDisposing);
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
In Setup.cs placed this code:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<SeekBar>("OnStopTrackingTouch", (seekbar) => new SeekbarStopTrackingTouchEventBinding(seekbar)));
}
Prepared property in my ViewModel and command executing function:
public IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> PlayProgressChanged
{
get
{
return new MvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs>(OnPlayProgressChange);
}
}
private async Task OnPlayProgressChange(SeekBar.StopTrackingTouchEventArgs e)
{
var progr = e.SeekBar.Progress;
await _playingService.SetTime((int) progr).ConfigureAwait(false);
}
In view layout, inside local:MvxBind linked my ViewModel command with evend name, provided in registry.RegisterFactory in Setup.cs
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/playprogress"
local:MvxBind="Progress ValueOfTimer; OnStopTrackingTouch PlayProgressChanged"
/>

Caliburn Micro Model executes, window doesn't show up

I'm encountering difficulties using Caliburn.Micro in my wpf project: any view's model's code is executed, but the window never shows up. Thus, the OnViewLoaded can show a MessageBox while the window remains invisible.
This is my bootstrapper:
public class Bootstrapper : BootstrapperBase
{
/// <summary>
/// IoC container for dependency injection
/// </summary>
private readonly SimpleContainer _container = new SimpleContainer();
private static Bootstrapper bootstrapper;
public Bootstrapper()
{
Initialize();
ConventionManager.AddElementConvention<PasswordBox>(ZBMS.PasswordHelper.BoundPasswordBox.BoundPasswordProperty, "Password",
"PasswordChanged");
}
protected override void Configure()
{
//Callback for redirecting pressed keys to the corresponding viewmodel
MessageBinder.SpecialValues.Add("$pressedkey", (context) =>
{
var keyArgs = context.EventArgs as KeyEventArgs;
return keyArgs?.Key;
});
// reset settings when debugger is attached (vs obviously)
if (Debugger.IsAttached)
Settings.Default.Reset();
_container.Singleton<IEventAggregator, EventAggregator>();
_container.Singleton<IWindowManager, WindowManager>();
//_Container.Singleton<ISettingsManager, NETSettingsManager>();
_container.PerRequest<LoadingScreenViewModel>();
}
protected override void OnStartup(object sender, StartupEventArgs e)
=> DisplayRootViewFor<LoadingScreenViewModel>();
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<Assembly> SelectAssemblies()
{
return new[]
{
Assembly.GetExecutingAssembly()
};
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return _container.GetAllInstances(service);
}
protected override void BuildUp(object instance)
{
_container.BuildUp(instance);
}
}

PRISM navigate to another View

I have a MainWindow which only contains the region for displaying other Views:
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion"/>
What I am trying to do, is to immediately when my MainWindowViewModel loads, to navigate to MainPageViewModel.
I have tried to implement interface INavigationAware such as following:
public void OnNavigatedTo(NavigationContext navigationContext)
{
_regionManager.RequestNavigate("ContentRegion", App.Experiences.DetailPage.ToString());
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
return true;
}
public void OnNavigatedFrom(NavigationContext navigationContext)
{
_regionManager.RequestNavigate("ContentRegion", App.Experiences.DetailPage.ToString());
}
But even when I set my breakpoints over these, they are never executed.
What am I doing wrong?
EDIT
Maybe I need to change my Bootstrapper logic? Here is how it looks like:
public class Bootstrapper: UnityBootstrapper
{
protected override System.Windows.DependencyObject CreateShell()
{
return Container.TryResolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterType(typeof(IDataRepository), typeof(DataRepository), null,new Microsoft.Practices.Unity.ContainerControlledLifetimeManager());
Container.RegisterTypeForNavigation<MainPage>(App.Experiences.MainPage.ToString());
Container.RegisterTypeForNavigation<DetailPage>(App.Experiences.DetailPage.ToString());
}
}
I had to edit my Bootstrapper logic such as following:
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
Prism.Regions.IRegionManager newRegion = Container.TryResolve<Prism.Regions.IRegionManager>();
newRegion.RequestNavigate("ContentRegion", App.Experiences.MainPage.ToString());
}

Wpf Caliburn.Micro view not loading

I am new to Wpf and Caliburn and I have the following scenario that i desire some help with, I am sure that the code is correct but this is not working as expected, to elaborate
My AppBootstrapper is thus:
public class AppBootstrapper : BootstrapperBase
{
private CompositionContainer _container;
public AppBootstrapper()
{
this.Initialize();
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
this.DisplayRootViewFor<ShellViewModel>();
}
protected override void Configure()
{
try
{
this._container = new CompositionContainer(new AggregateCatalog(new DirectoryCatalog(".", "*")));
var batch = new CompositionBatch();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(new EventAggregator());
batch.AddExportedValue(this._container);
this._container.Compose(batch);
}
catch (Exception ex)
{
this._log.Error(ex.ToString());
}
}
protected override object GetInstance(Type service, string key)
{
try
{
var contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(service) : key;
var exports = this._container.GetExportedValues<object>(contract);
if (exports.Any())
{
return exports.First();
}
throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract));
}
catch (Exception ex)
{
if (ex is ReflectionTypeLoadException)
{
var typeLoadException = ex as ReflectionTypeLoadException;
var loaderExceptions = typeLoadException.LoaderExceptions;
if (loaderExceptions != null)
{
this._log.Error(loaderExceptions.First().ToString());
}
}
throw;
}
}
protected override IEnumerable<object> GetAllInstances(Type service)
{
return this._container.GetExportedValues<object>(AttributedModelServices.GetContractName(service));
}
protected override void BuildUp(object instance)
{
this._container.SatisfyImportsOnce(instance);
}
}
My ShellViewModel is as follows (i have removed some of the code to keep it short)
[Export(typeof(ShellViewModel))]
public class ShellViewModel : Conductor<IScreen>,
IHandle<NavigationEvent>,
IHandle<WindowCloseEnabledEvent>,
IShell,
IShellViewModel
{
private readonly IEventAggregator _eventAggregator;
[ImportingConstructor]
public ShellViewModel(IEventAggregator eventAggregator)
{
this._eventAggregator = eventAggregator;
}
[Import(typeof(LoginViewModel))]
public Lazy<IScreen> LoginViewModel { get; set; }
public void Loaded()
{
this._eventAggregator.Subscribe(this);
this.ActivateItem(this.LoginViewModel.Value);
}
}
I have an event trigger in the ShellView.xaml that triggers the Loaded method above.
I also have a LoginViewModel and LoginView (note that these are in ViewModels and Views folders in the project.
The important bit of LoginViewModel is
[Export(typeof(LoginViewModel))]
public class LoginViewModel : Screen, IAnimatableViewModel, ILoginViewModel
{
protected override void OnViewLoaded(object view)
{
this._view = view;
IoC.Get<ShellViewModel>().DisableClose = true;
base.OnViewLoaded(view);
}
}
And the LoginView.xaml at the moment simply displays some text.
I am using MEF as the DI container, the issue is that when I run up the application the ShellView is loaded which should load the LoginView into it but the LoginView does not load (or doesn't display)
If anyone can help it would be greatly appreciated.

Categories

Resources