Resetting ViewModels created by Caliburn Micro - mvvm

I am trying to implement Logout functionality in a Windows Store app that I am working on which is using Caliburn Micro.
The challenge that I am having is that if a user logs out, and then logs in again as another user, the ViewModels instantiated at app launch still exists in memory and have references to old models. Hence, Views are being displayed with stale data of the first user.
To better explain myself:
public class LoginViewModel : Screen
{
private User _model;
// Property gets initialised only on instantiation of ViewModel class
public User Model
{
get { return _model; }
set
{
if (Equals(value, _model)) return;
_model = value;
NotifyOfPropertyChange(() => Model);
}
}
// ViewModel constructor - instantiated once only by Caliburn on startup
public LoginHeaderViewModel(IAuthService authService)
{
Model = _authService.User;
}
}
When my app first launches, Caliburn will automatically instantiate LoginViewModel and run its constructor, which in turn fetches the current logged in user. After a user is logged out, and another user logs in, LoginViewModel is not instantiated again at runtime because it already exists. The Model property is not re-evaluated and hence the associated View is not told to refresh itself.
I tried re-creating Caliburn's WinRTContainer on logout but the application started behaving funny. I was suspicious of going down this approach anyway so didn't look too much into it just yet.

The way I resolved my initial problem (and there are many ways to do so) is using Caliburn event aggregation.
I first start off by creating an event:
public class LoginEvent
{
public bool IsloggedIn { get; set; }
public LoginEvent(bool isloggedIn)
{
IsloggedIn = isloggedIn;
}
}
When a user logs in, I make sure that I publish an instance of the login event:
public LoginViewModel(IEventAggregator events)
{
_events = events;
}
public async void SignIn()
{
// Do login logic ...
_events.Publish(new LoginEvent(true));
}
In the ViewModel that needs to rest itself whenever a new user logs in, ensure that I subscribe to the LoginEvent to reinitialise my models when it is triggered. A ViewModel listening to events must implement the IHandle<T> interface.
public class DependentViewModel : IHandle<LoginEvent>
{
public DependentViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
InitialiseViewModel();
}
public async void InitialiseViewModel()
{
// Initialise all your model objects here...
}
public async void Handle(LoginEvent ev)
{
if (ev.IsloggedIn)
{
InitialiseViewModel();
}
}
}

What Lifestyle do your ViewModel objects have?
There are also override methods such as OnDeactivate which you could use to clean up user specific settings, take a look here specifically at the screens section for methods that you can override.

Related

How to initialize ViewModel with CommunityToolkit.Mvvm

We have a Xamarin.Forms app with FreshMvvm. Now, as Xamarin.Forms will not get support beginning next year, I am re-writing the app with .Net Maui. For MVVM pattern, I am trying to use CommunityToolkit.Mvvm. But I wonder how I can initialize the viewmodel now. With FreshMvvm I could override Init(), but CommunityToolkit.Mvvm does not seem to have anything like this. What is the right way to initialize the viewmodel asynchronously, as there is no async constructor?
In FreshMVVM, since the model has impelmented the FreshBasePageModel, you can override the Init() method and initialize data like the pseudo code below:
public override void Init (object initData)
{
//initialize data
}
However, in CommunityToolkit.Mvvm, you can set the data whatever you want in the default constructor like below:
public partial class MainPage : ContentPage
{
public MainPage(MainPageViewModel vm)
{
InitializeComponent();
BindingContext = vm;
Initialize();
}
public async void Initialize()
{
//await operation
}
}
Official reference link:https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/introduction

mvvmcross touch command binding in android

I'm looking for a way to do a "Touch" command binding between axml and ViewModel, or some else like FocusChanged etc.
A simple "Click" command works fine like so:
local:MvxBind="{'Touch':{'Path':'CameraButtonCommand'}}" />
public IMvxCommand CameraButtonCommand
{
get
{
return new MvxRelayCommand(
() =>
{
RequestNavigate<AugRealityViewModel>(true);
})
;
}
}
However, I've tried other event types for the controll(in this case it's ImageButton) and they are not being processed. When I've checked the events listings in the View Class I see those:
public event EventHandler Click;
public event EventHandler<View.CreateContextMenuEventArgs> ContextMenuCreated;
public event EventHandler<View.FocusChangeEventArgs> FocusChange;
public event EventHandler<View.KeyEventArgs> KeyPress;
public event EventHandler<View.LongClickEventArgs> LongClick;
Only Click event has the general EventHandler attached to it, while other have genericed EventHandlers, and I'm wondering if that's the reason why it doesn't work.
I've also tried to attach a method to those events in the View class getting the proper control by FindViewById method and it works as expected this time around. But somehow I can't do it in the axml through Commands.
Also one more thing. The "Click" event is sending the "EventArgs" object as one of the parameters, and also the object reference. I can see that with ease if I do this behaviour in View Class, but when I do this by binding, I don't see those arguments when I'm processing the Command in ViewModel.
The framework can automatically bind any events which require EventHandler types. However, for any events which require a templated EventHandler (with custom EventArgs) then you are correct - you'll need to include a custom Binding.
The good news is that custom bindings are easy to write and to include.
For example, to bind:
public event EventHandler<View.LongClickEventArgs> LongClick;
you can include something like:
public class LongPressEventBinding
: MvxBaseAndroidTargetBinding
{
private readonly View _view;
private IMvxCommand _command;
public LongPressEventBinding(View view)
{
_view = view;
_view.LongClick += ViewOnLongClick;
}
private void ViewOnLongClick(object sender, View.LongClickEventArgs eventArgs)
{
if (_command != null)
{
_command.Execute();
}
}
public override void SetValue(object value)
{
_command = (IMvxCommand)value;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
_view.Click -= ViewOnLongClick;
}
base.Dispose(isDisposing);
}
public override Type TargetType
{
get { return typeof(IMvxCommand); }
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.OneWay; }
}
}
Which can be configured in setup using something like:
protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
base.FillTargetFactories(registry);
registry.RegisterFactory(new MvxCustomBindingFactory<View>("LongPress", view => new LongPressEventBinding(view)));
}
Note that you can't write a single class that binds to all the different event types - as the compiler requires you to include the correct Type for the EventArgs. However, you could fairly easily change public class LongClickEventBinding to something like public class CustomEventBinding<TViewType, TEventArgsType> if you wanted to.
With regards to what argument you should pass into the IMvxCommand Execute method, I guess this depends a bit on the method in question, and it also depends on whether you need the ViewModel to support multiple platforms, or whether it is just for Android.

AOP using Windsor and bulk registering classes

I am trying to configure an application such that types from assemblyA can be used by my console to allow for logging in an AOP style. The JournalInterceptor will just write out method calls, input and maybe output arguments to a log file or datastore of some kind.
I can register one type at a time but I would like to register all types in one go. Once I get going I may add some filtering to the registered types but I am missing something.
I am trying to use Classes.FromAssemblyContaining but am not sure how to get at an IRegistration instance for the call to WindsorContainer::Register
Any clues?
// otherAssembly.cs
namespace assemblyA
{
public class Foo1 { public virtual void What(){} }
public class Foo2 { public virtual void Where(){} }
}
// program.cs
namespace console
{
using assemblyA;
public class JournalInterceptor : IInterceptor {}
public class Program
{
public static void Main()
{
var container = new Castle.Windsor.WindsorContainer()
.Register(
Component.For<JournalInterceptor>().LifeStyle.Transient,
// works but can't be the best way
Component.For<Foo1>().LifeStyle.Transient
.Interceptors<JournalInterceptor>(),
Component.For<Foo2>().LifeStyle.Transient,
.Interceptors<JournalInterceptor>(),
// how do I do it this way
Classes.FromAssemblyContaining<Foo1>()
.Pick()
.LifestyleTransient()
.Interceptors<JournalInterceptor>()
);
Foo1 foo = container.Resolve<Foo1>();
}
}
}
Implement a Pointcut. In Castle Windsor this is done by implementing the IModelInterceptorsSelector interface.
It would go something like this:
public class JournalPointcut : IModelInterceptorsSelector
{
public bool HasInterceptors(ComponentModel model)
{
return true; // intercept everything - probably not a good idea, though
}
public InterceptorReference[] SelectInterceptors(
ComponentModel model, InterceptorReference[] interceptors)
{
return new[]
{
InterceptorReference.ForType<JournalInterceptor>()
}.Concat(interceptors).ToArray();
}
}
Then register the Interceptor and the Pointcut with the container:
this.container.Register(Component.For<JounalInterceptor>());
this.container.Kernel.ProxyFactory.AddInterceptorSelector(new JournalPointcut());
For in-depth explanation, you may want to see this recording.

Service Class + Instantiating new classes

I wanted to know if this was thread safe/ good practice. My IOC is ninject, everything service layer call is via the default setting (In transient scope I think?).
Question, is instantiating new FileAllocation(loggedonuser,_repo) correct? The best way? What is the best way to do this? This is a domain class that holds logic that could be called from various services, there are usually a few database calls involved, most of the time no persistance is necessary...
Anyway, I call my service method via an interface e.g.
void SaveFile(int reportid, stream file); //Interface name: IReportFileService
public Class FileService: Servicebase, IReportFileService
{
private readonly IRepoSession _repo;
public FileService(IUserSession user, IRepoSession repo, IUpdateSession update)
: base(user,update)
{
_repo = repo;
}
//save file if users 'counter' is ok..
public void SaveFile(int reportid, stream file)
{
//here I want to instantiate a new class that I store in my domain and store the counters
//etc and do related db calls to check up relevant values
//note loggedonuser is a prop on my *base class*
var userChecks = new FileAllocation(loggedonuser,_repo);
userChecks.CountEmUp(); //exception is thrown if 0, less than "limit" etc...
base.update(userChecks.mycompany); //persist
base.commit(); //base class method includes try, catch block...
}
}
public class FileAllocation
{
private readonly IRepoSession _repo;
private readonly Loggedonuser _user;
private int CompanyUploads;
private int UserUploads;
public Company mycompany;
public FileAllocation(Loggedonuser user, IRepoSession repo)
{
_repo = repo;
_user = user;
}
public void CountEmUp()
{
//do error checking,
//load up other tables can user upload - permissions, count is ok etc...
// check the upload type if of certain type we cannot proceed - call another method on this class
//set myCompany variable to new limits etc...
}
}
Base Service includes a prop, I dont want to instantiate this from other services i.e. more that once, how do I avoid that?
private LoggedonuserDTO _currentuser = null;
protected LoggedonuserDTO loggedonuser
{
get
{
if (_currentuser == null)
{
_currentuser = _user.GetCurrentUser(); //make db call here...
}
return _currentuser;
}
}
#Darin suggested:
public interface IFileAllocation
{
CountEmUp(Loggedonuser currentuser);
}
//pass in loggedonuser to any method that requires it...
public class FileAllocation: IFileAllocation
{
CountEmUp(Loggedonuser currentuser)
{
//do whatever here...
}
}
var userChecks = new FileAllocation(loggedonuser,_repo);
introduces a strong coupling between the FileService and the FileAllocation classes. If this is not a problem for you then you can leave it that way. Otherwise you could abstract the operations of this FileAllocation class into an interface and then inject it into FileService. This way the FileService is weakly coupled with FileAllocation and could be reused in different contexts and unit tested in isolation.

Ninject PerRequest injection

I have a MVC 2.0 application using Ninject.Web.Mvc2 and a repository pattern (built over an entity framework model ). I am trying to create a new ObjectContext that will only live for the duration of the request. I am attempting to accomplish this in the following manner:
protected override IKernel CreateKernel(){
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly());
return kernel;
}
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
}
I then ran out of ideas for keeping this generic enough so I set to work in the BeginRequest:
protected void Application_BeginRequest()
{
string EntityConnectionString = ConfigurationManager.ConnectionStrings["Entities"].ConnectionString;
HttpContext.Current.Items.Add(_PerRequestContextObjectKey, new EntityFrameworkContextWrapper(EntityConnectionString));
this.Kernel.Bind<IUserRepository>().To<UserRepository>().WithConstructorArgument("ContextWrapper", HttpContext.Current.Items[_PerRequestContextObjectKey]);
}
The Wrapper class is just a generic object for including anything I want to kill at the end of request. In this specific case, I use it to create my new ObjectContext and also implement IDisposable so I can do the following:
protected void Application_EndRequest()
{
foreach (var Item in HttpContext.Current.Items)
{
if (Item.GetType() == typeof(IPerRequestLifetimeObjectWrapper))
{
(Item as IPerRequestLifetimeObjectWrapper).Dispose();
}
}
}
I'm sure it's not the prettiest way of doing this, but at this point I'm trying to get moving since I've spent so much time "learning" all this stuff.
My Controller is then injected like so:
public class AdminUserController : Controller
{
// Mark for Ninject
[Inject] public IUserRepository _userRepo { get; set; }
public ViewResult Index( )
{
return View(_userRepo.Get);
}
public ViewResult Edit(Guid UserUID)
{
return View(_userRepo.GetById(UserUID));
}
}
and my Repository gets injected as well:
[Inject]
public UserRepository(EntityFrameworkContextWrapper ContextWrapper )
// Mark for Ninject Dependency Injection
// Must receive Wrapper that contains active ObjectContext
{
_db = ContextWrapper.Entities; //Not actually named this, just easier for typing right now
}
When my Controller calls the Get method inside my UserRepository object the first time it works great. If I hit refresh ( or I'm guessing postback as well ), _db is Null. When I try to step through the debugger, I find that the Controller Index( ) method is being called before a Application_BeginRequest() is called. I thought I had an understanding of the "pipeline" ( I'm used to calling things page lifecycle from WebForms ), but now I'm a bit lost. Can someone elaborate on where my brain has some wires crossed? Like I said, this probably isn't the prettiest method, but I've only had about a week and a half to learn MVC, DI with Ninject, Repository, and Entity Framework so please don't feel like you're talking down to me if it seems like I broke something very basic.
Why don't you simply use InRequestScope? What you do is adding a new binding for each request. This will lead to severe problems.
See https://github.com/ninject/ninject.web.mvc/wiki/Unit-of-work-pattern-with-nhibernate
It's NHilbernate but you can do the same with EntityFramework