Not getting Buttons updated using MVVM and Autofac in Windows Phone 8 - c#

I'm building a small app that is based on MVVM and uses Autofac as a dependency injector.
I've created the VMBase which all the ViewModels depends from and I'm also using VMLocator to inject all the dependencies (using Autofac as stated before).
public class VMLocator
{
IContainer container;
public VMLocator()
{
var builder = new ContainerBuilder();
builder.RegisterType<VMRetrieveInfo>();
container = builder.Build();
}
public VMRetrieveInfo RetrieveInfoViewModel
{
get { return container.Resolve<VMRetrieveInfo>(); }
}
}
Got a DelegateCommand class that handles Raises of CanExecute:
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(null, new EventArgs());
}
My problem comes when I try to put a button using DelegateCommand class that changes its state using a CanExecute method which returns a variable that is changed when I press another button.
Changing the value of the variable doesn't seem to launch any event that changes the CanExecute result (the CanExecute method is not even called) and I'm pretty lost.
public ICommand NavigateCommand
{
get { return navigateCommand; }
}
public void NavigateCommandExecute()
{
}
public bool NavigateCommandCanExecute()
{
return canCallWS;
}
Running a similar app without dependency injection (Autofac) has worked for me before.
Any help?
Thanks

In the DelegateCommand class you have a CanExecuteChanged event, the same way you did a RaisePropertyChanged method to launch the PropertyChanged in your ViewModel base, you need to do a RaiseCanExecuteChanged method in your DelegateCommand class to launch the CanExecuteChanged event when you change the condition...
In WPF we have a CommandManager who makes this job for us without need to launch the event. In Windows Phone we need to launch the event. This is not an autofac or DI issue, is the way command can execute works in Silverlight/Windows Phone.
Hope this helps!

Related

Using ICommand in Windows Workflow Custom Activities

I have used Relay Commands quite a bit in WPF projects but I am currently working on Windows Workflows and I am making a custom activity with its own designer. I want a button on my design to add new fields if pressed. I have spent days trawling the internet but it seems windows workflow has defeated me. This is what I have and it does not work, if anyone knows why please help.
[Designer(typeof(MyCustomActivityDesigner))]
public sealed class MyCustomActivity : AsyncCodeActivity
{
public MyCustomActivity()
{
AddSpecificParameter = new DelegateCommand(AddParameter);
}
public DelegateCommand AddSpecificParameter { get; set; }
public void AddParameter()
{
//add value to an obervable collection
}
}
Relay command implementation:
public class DelegateCommand :ICommand
{
//Parameterless constructor needed as windows workflow serialises it
public DelegateCommand()
{ }
private readonly Action _action;
public DelegateCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
And the xaml is simply a button with a command binding but due to windows workflow weird stuff it goes through ModelItem (at least for the other bindings I have)
<Button Command="{Binding Path=ModelItem.AddSpecificParameter}"/>
So the key differences from code that I have in WPF apps that works is the parameter-less constructor but this shouldn't effect it? Tested this, parameter-less constructor works fine in WPF apps,so it has to be due to the ModelItem not being able to handle commands?
Also the ModelItem Binding path works for all the other bindable properties on the viewmodel (for example strings on labels)
Note: The activities are tested/displayed in a rehosted designer.

How to correctly send event-messages between modules in Prism framework?

I am trying to build a WPF Prism bases app using MVVM design pattern.
When my app first starts, I want to require the user to login. Once logged in, I want to show the default landing page with the user name and buttons.
My thought, when a user login, I would publish an event called UserLoggedIn then on the home view-model, I would listen for that event. When the event is triggered, I would show the landing/home view.
So I created the event like so
public class UserLoggedIn : PubSubEvent<User>
{
}
Then in the LoginViewModel I handle the login and publish the event like so
private void HandleLogin(LoginView loginView)
{
try
{
User user = AuthenticationService.Authenticate(Username, loginView.GetPassport());
IUserPassport passport = PassportManager.Get(user.Username);
if (passport == null)
{
// Create a new session
passport = new UserPassport(new CustomIdentity(user), RegionManager);
}
// Assign the current session
PassportManager.SetCurrent(passport);
// Trigger the event
EventAggregator.GetEvent<UserLoggedIn>().Publish(passport);
// Deactivate the login view
RegionManager.GetMainRegion().Deactivate(loginView);
}
catch (Exception ex)
{
//Do something with the error
}
}
Finally in my HomeViewModel aka my landing view, I have the following code to listen for the UserLoggedIn event.
public class HomeViewModel : BindableBase
{
protected IUnityContainer Container { get; set; }
protected ICoreRegionManager RegionManager { get; set; }
private IEventAggregator EventAggregator { get; set; }
public HomeViewModel(IUnityContainer container, ICoreRegionManager regionManager, IEventAggregator eventAggregator)
{
Container = container;
RegionManager = regionManager;
EventAggregator = eventAggregator;
eventAggregator.GetEvent<UserLoggedIn>().Subscribe(ShowTheLangingPage);
}
private void ShowTheLangingPage(User user)
{
var homeView = Container.Resolve<HomeView>();
RegionManager.AddToMainRegion(homeView);
FullName = user.FirstName;
}
// I am using PropertyChange.Fody package, so this propery will automaticly raise the PropertyChange event.
public string FullName { get; set; }
}
Problem is the ShowTheLangingPage method never get triggered in my HomeViewModel as expected.
I made sure the the View HomeView and HomeViewModel are wired up correctly, by directly loading the HomeView on module initialization for testing.
Additionally, if add Container.Resolve<HomeView>(); just before I publish the event, the ShowTheLangingPage I called. Its like I have to resolve the HomeView manually for it to listen for the event.
How can I correctly listen for the UserLoggedIn event so i can show the corresponding view.
So I can learn the better/recommended way, is it better to show the landing view from the LoginViewModel instead of using event/listener.... and why? Also, if showing the landing view directly from the LoginViewModel then what is the recommended method to navigate; using Region.Add() method or RegionManager.RequestNavigate?
is it better to show the landing view from the LoginViewModel instead of using event/listener....
Yes.
and why?
Because that's what services (like the IRegionManager) are for, doing stuff for your view models and other services. Also, you have noticed, events can only be subscribed to by living objects.
Also, if showing the landing view directly from the LoginViewModel then what is the recommended method to navigate; using Region.Add() method or RegionManager.RequestNavigate?
If anything, a third class should listen for UserLoggedIn, but that's no gain over using the IRegionManager directly. In fact, it's even worse, because you have to artifically create this class. Side note: if you wait for the garbage collector after Container.Resolve<HomeView>(); and before logging in, you won't go to the landing page, because there's no subscriber (again).

Xamarin Forms Previewer not working

I am trying to use Xamarin Forms Previewer. But it display error as shown in attached image. How can I solve this?
As you can see. It crashes because it cannot resolve your dependency (that you inject with the an Autofac ioc Container).
If your dependency is resolved at runtime, you probably have to mock it at "design time" by injecting a different implementation.
See http://blog.mzikmund.com/2017/07/checking-for-design-mode-in-xamarin-forms/
public static class DesignTimeHelper
{
public static bool DesignModeOn { get; private set; } = true;
}
public static void TurnOffDesignMode()
{
DesignModeOn = false;
}
}
And you'll have to do something like that on your IOC builder:
if(DesignTimeHelper.DesignModeOn)
builder.Register<INotificationManager, MockNotificationManager>();
else // your real dependency
If you have code in your xaml to wire the View to the View Model, try disabling the code.
In my xaml, I had to do this:
utility:ViewModelFetcher.AutoWireViewModel="True"
to
utility:ViewModelFetcher.AutoWireViewModel="False"
and the previewer worked.

How do i detect click event in .Core from view.cs in .Droid using MVVMCross?

I'm currently working on a login screen and I wish for an ImageView to become visible upon "login" button click and rotate with an animation. I know how to animate the object, but since the animation is a unique android feature I have to initiate it from LoginView.cs in the .Droid project with e.g. a function and not simply from LoginViewModel in the .Core project.
I know how to treat MvxCommands in LoginViewModel.cs in .Core (and other MvxBind-ings), but I have no clue how to make the LoginView.cs in .Droid to detect this click event and play the animation from there. I other words, how do I make LoginView.cs in .Droid detect any changes happening in LoginViewModel.cs in .Core?? I have been Googlin' for several hours without any luck. Is this even possible? A solution would be god-sent.
Thanks in advance
The simplest way to handle ViewModel -> View communication is with a Func/Action delegate. Setup a property in your VM that takes a delegate, then assign the delegate in the view. You can then call the delegate from the VM.
Something like:
public class ViewModel {
public Action ClickDelegate { get; set; }
public ICommand ClickCommand {
get { return new MvxCommand(() => {
// call the action method to start animation
ClickDelegate?.Invoke();
};
}
}
}
public class MyView {
protected override OnCreate() {
// register delegate with VM
ViewModel.ClickDelegate = () => { StartAnimation(); };
}
}

View is not locating the Viewmodel with default VML in MVVMCross

I'm trying to create a Portable Class such that I can use that across the platforms. It is working fine in Windows Phone 8.1 App. But when it comes to Android, then it is showing the Viewmodel as null and DataContext as Null in debugger which breaks the application debugger. When I create another viewmodel and view to test the app, its working fine on android too. What can be the possible reasons.
EDIT : It is crashing due to the constructor , in which I am passing the business Logic instance. So , Constructor is necessary i think but in that case it is crashing.I am not trying to resolve the ViewModel , i am trying to resolve the Service instance in ViewModel and for the purpose of MVVM, I am keeping the Service out from Droid Project so base.OnCreate(bundle) does not come into scene anyways.
public BookViewModel(ILogic _logic)
{
logic = _logic;
//var ss= Mvx.Resolve<ILogic>();
//var x = Mvx.CanResolve<ILogic>();
_details = logic.Read();
}
Below is the Logic Code :
public class Logic : ILogic
{
#region Attributes
List<Detail.Detail> _details = new List<Detail.Detail>();
DataLayer.DataLayer dl = new DataLayer.DataLayer();
#endregion
#region .ctor
public Logic()
{
populateList();
}
#endregion
#region Methods
private void populateList()
{
_details = dl.Access();
}
Below is the App.cs in ViewModel in which CanResolve is giving False
public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
#region Methods
public override void Initialize()
{
Mvx.RegisterType<ILogic, Logic>();
var ss = Mvx.CanResolve<ILogic>();
RegisterAppStart<ViewModels.BookViewModel>();
}
#endregion
}
There are a few questions and answers around similar to this - e.g. similar to MVVMCross ViewModel construction failure notifications
The basic answer is that MvvmCross cannot resolve the ViewModel during the constructor - you have to wait until after the base.OnCreate(bundle) call - at this point the ViewModel will be resolved.
There's also a bit more about when ViewModel's are located in Who should create view model instances in MvvmCross and CoreDispatcher.HasThreadAccess "breaking change" (and probably a few other places too)

Categories