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);
}
}
Related
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.
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"
/>
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());
}
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.
Recently I took an interest in developing PRISM WPF applications. Now I'm trying to load my modules from a DLL I make after building the Modules Project (Wpf User Control Library). During the build of the modules project I copy the DLL in the debug folder (copy: xcopy /y "$(TargetPath)" "$(SolutionDir)FooBar\$(OutDir)Modules\"). Next I configure the bootstrapper and I think there is were I lost it.
I'll attach my code below.
Bootstrapper
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
var shell = ServiceLocator.Current.GetInstance<Shell>();
return shell;
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)this.Shell;
App.Current.MainWindow.Show();
}
protected override IModuleCatalog CreateModuleCatalog()
{
return base.CreateModuleCatalog();
}
protected override void ConfigureModuleCatalog()
{
var moduleCatalog = new DirectoryModuleCatalog();
moduleCatalog.ModulePath = Environment.CurrentDirectory + #"\Modules";
ModuleCatalog = moduleCatalog;
}
protected override void InitializeModules()
{
base.InitializeModules();
}
protected override ILoggerFacade CreateLogger()
{
return base.CreateLogger();
}
}
Shell.xaml.cs
protected readonly IModuleCatalog _moduleCatalog;
public Shell(IModuleCatalog moduleCatalog)
{
this._moduleCatalog = moduleCatalog;
InitializeComponent();
}
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanging, INotifyPropertyChanged,IModule
{
//Implementation INotify etc..
public void Initialize()
{
}
}
So I'm wondering why my ModuleCatalog.Modules is always 0. Can someone help me out ?
Is your module catalog null? or contains no module?
check that Environment.CurrentDirectory + #"\Modules"; returns the correct path and all DLL are in.
Do you really need to load them from dir? Don't you know which modules will be loaded?
I usualy do that:
public partial class Bootstrapper: UnityBootstrapper
{
protected override void ConfigureContainer()
{
base.ConfigureContainer();
}
protected override IModuleCatalog CreateModuleCatalog()
{
// Create the module catalog from a XAML file.
return Microsoft.Practices.Prism.Modularity.ModuleCatalog.CreateFromXaml(new Uri("/Shell;component/ModuleCatalog.xaml", UriKind.Relative));
}
protected override DependencyObject CreateShell()
{
// Use the container to create an instance of the shell.
ShellView view = Container.TryResolve<ShellView>();
// Display the shell's root visual.
//view.ShowActivated = false;
view.Show();
return view;
}
}
and my modulecatalog
<prism:ModuleCatalog xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Modularity;assembly=Microsoft.Practices.Prism">
<prism:ModuleInfo Ref="Connexion.dll"
ModuleName="Connexion"
ModuleType="Connexion.ModuleInit, Connexion, Version=1.0.0.0" />
<prism:ModuleInfo Ref="Tools.dll"
ModuleName="Tools"
ModuleType="Tools.ModuleInit, Tools, Version=1.0.0.0" />
<prism:ModuleInfo Ref="DrawingModule.dll"
ModuleName="DrawingModule"
ModuleType="DrawingModule.ModuleInit, DrawingModule, Version=1.0.0.0"
InitializationMode="WhenAvailable">
<prism:ModuleInfo.DependsOn>
<sys:String>Connexion</sys:String>
<sys:String>Tools</sys:String>
</prism:ModuleInfo.DependsOn>
</prism:ModuleInfo>
<prism:ModuleInfo Ref="Sis.dll"
ModuleName="Sis"
ModuleType="Sis.ModuleInit, Sis, Version=1.0.0.0"
InitializationMode="WhenAvailable">
<prism:ModuleInfo.DependsOn>
<sys:String>Connexion</sys:String>
<sys:String>Tools</sys:String>
<sys:String>DrawingModule</sys:String>
</prism:ModuleInfo.DependsOn>
</prism:ModuleInfo>
and all modules have the buildAction: copy "$(TargetPath)" "$(SolutionDir)Shell\$(OutDir)"