I am using autofac for dependency injection and I need to override the navigation function. in order to do that I did
Locator.cs(where contain the Cs files)
private readonly ContainerBuilder _builder;
public locator()
{
_builder = new ContainerBuilder();
register();
Container = _builder.Build();
}
public IContainer Container { get; set; }
private void register()
{
_builder.RegisterType<vm>().As<Ivm>();
_builder.RegisterType<Vm1>();
_builder.RegisterType < basevm>();
_builder.RegisterType<MainPage>();
_builder.RegisterType<xa>();
}
In my app.Xaml.cs
In constructor
public App()
{
InitializeComponent();
locator locator1 = new locator();
Container = locator1.Container;
MainPage = new NavigationPage(Container.Resolve<MainPage>());
}
public static IContainer Container;
then I tried to override the navigation func in my main page code behind it cannot be override. what I am missing and where i use this
public abstract void Navigate(SelectedItemChangedEventArgs e);
public override async void Navigate(SelectedItemChangedEventArgs e)
{
xa patientListViewPage = App.Container.Resolve<xa>();
await Navigation.PushAsync(patientListViewPage);
}
why this is not working. I occur this error
'MainPage.Navigate(SelectedItemChangedEventArgs)': no suitable method found to override
You can derive from NavigationPage reference.
public class CustomNavigationPage : NavigationPage
{
//You can define your container here.
public CustomNavigationPage()
{
//You can resolve here
}
}
and also you can look
here
I Can think of a better way You are using Autofac so you can have a generic method that can help the cause.
public static async Task NavigateAsync<TContentPage>(INavigation navigation ) where TContentPage : ContentPage
{
var contentPage = App.Container.Resolve<TContentPage>();
await navigation.PushAsync(contentPage, true);
}
Also If you need to pass a parameter You can modify it like this
public static async Task NavigateAsync<TContentPage, TNavigationParameter>(INavigation navigation,
TNavigationParameter navParam,
Action<TContentPage, TNavigationParameter> action = null) where TContentPage : ContentPage
{
var contentPage = App.Container.Resolve<TContentPage>();
action?.Invoke(contentPage, navParam);
await navigation.PushAsync(contentPage, true);
}
Related
I am working on a Xamarin.Android project developed by another developer. I got to know that they have used Dependancy Injection. There is a class like this
public class RemoteSupportHandler : DataAccessor<FwDataContext>, IRemoteSupportSettingHandler
{
private RemoteSupportSetting[] _cached;
public RemoteSupportHandler(IDatabaseController db, ILogger logger, IPerfLogger perfLogger)
: base(db, logger, perfLogger)
{
}
public async Task<RemoteSupportSetting> GetRemoteSupportDemoVideoUrlAsync()
{
return await WithDataContextAsync(ctx =>
{
return (from row in ctx.RemoteSupportSettings
where row.ParamName == "DEMO_VIDEO"
select new RemoteSupportSetting
{
ParamName = row.ParamName
}).FirstOrDefault();
});
}
}
In another file, they have registered this class with UnityContainer. Now I want to call this GetRemoteSupportDemoVideoUrlAsync() method from my Activity. I know I cannot create an object using this constructor. I have no idea how I should I do this.
Registration Code
public class Registrar : IRegistrar {
protected virtual void OnApplyInitializedRegistrations(IUnityContainer container) {
container.RegisterType<IRemoteSupportSettingHandler, RemoteSupportHandler>(new ContainerControlledLifetimeManager());
}
}
public interface IRegistrar
{
void ApplySessionRegistrations(IUnityContainer container);
void ApplyInitializedRegistrations(IUnityContainer container);
}
UPDATE 2
public abstract class MyApplication : Application, IPlatformApplication {
public IUnityContainer AppUnityContainer => _container;
protected virtual void OnLaunched()
{
IActivityService activityService = new ActivityService(this);
AppUnityContainer.RegisterInstance(activityService);
AppUnityContainer.RegisterType<IRegistrar, PlatformRegistrar>();
}
}
I'm using VS 17 for Xamarin Forms. I've set up Prism in my Xamarin.Forms app and I just added a reference to my Api interface (in ViewModel Constructor) and it makes the app stop navigation to the second page. I need to do this in order to pass parameters etc. I followed this guide:
https://blog.qmatteoq.com/prism-for-xamarin-forms-basic-navigation-and-dependency-injection-part-2/
This is what I did to make the navigation stop working:
private readonly IService _Service;
private ObservableCollection<TodoItem> _topSeries;
public ObservableCollection<TodoItem> TopSeries
{
get { return _topSeries; }
set { SetProperty(ref _topSeries, value); }
}
This is the constructor:
public SecondPageViewModel(IService Service, INavigationService navigationService)
{
_Service = Service;
_navigationService = navigationService;
}
So I cant even reach the above viewmodel because of the above code that I added. I tried to put break points on the DelegateCommand (on first ViewModel) but it just stops after InitializeComponent(); and then nothing happens. No error messages! Thanks!
Update:
My Service class that fetches data:
public class Service : IService
{
public List<TodoItem> TodoList { get; private set; }
HttpClient client;
Service()
{
client = new HttpClient();
client.MaxResponseContentBufferSize = 256000;
}
public async Task<List<TodoItem>> DataAsync()
{
TodoList = new List<TodoItem>();
var uri = new Uri(string.Format(Constants.RestUrl, string.Empty));
try
{
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
TodoList = JsonConvert.DeserializeObject<List<TodoItem>>(content);
Debug.WriteLine(content);
}
}
catch (Exception ex)
{
Debug.WriteLine(#"ERROR {0}", ex.Message);
}
return TodoList;
}
}
This is my App.Xaml.cs
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<View.MainPage, MainPageViewModel>();
containerRegistry.RegisterForNavigation<View.SecondPage, SecondPageViewModel>();
containerRegistry.Register<IService, Service>();
}
My Interface:
public interface IService
{
Task<List<TodoItem>> DataAsync();
}
This is how I navigate (click from listview):
private EventItem _selectedEvent { get; set; }
public EventItem SelectedEvent
{
get { return _selectedEvent; }
set
{
if (_selectedEvent != value)
{
if (Device.RuntimePlatform == Device.iOS)
{
_selectedEvent = null;
}
else
{
_selectedEvent = value;
}
NavigationParameters navParams = new NavigationParameters();
navParams.Add("PassedValue", _todoItem.name);
_navigationService.NavigateAsync("SecondPage", navParams);
}
}
}
Edit:
When I debug without the ApiService code the command is taking me to new new constructor in the new viewmodel. With the code it does not reach the contructor.
According to your code you have declared constructor like this:
Service()
{
// ...
}
You didn't set access modifier, therefore the default one is internal. Here is the definition:
Internal types or members are accessible only within files in the same
assembly.
Most likely you have your Service.cs declared in another Assembly and Prism can't access its constructor.
Your navigation doesn't work because dependency injection fails. To fix it, just change your access modifier to public:
public Service()
{
// ...
}
I have the next problem, i dont understand why this code dont work i think is because i dont injectate the class of constructor by autofac but i dont know how do that, can us help me to do that the better way?
Before I add the generator this work if i comment the generator code in service work.
This is my code:
I have a class Controller that invoke a serv:
public class ZonesController : Controller
{
private IZoneService zoneService;
public ZonesController(IZoneService zoneService)
{
this.zoneService = zoneService;
}
[HttpGet]
//Do work
}
This is the service and interface:
public class ZoneService : IZoneService
{
private readonly IZoneRepository zoneRepository;
private readonly IDtoFactory dtoFactory;
private readonly ZoneGenerator zoneGenerator;
public ZoneService(IZoneRepository zoneRepository,
IDtoFactory dtoFactory,
ZoneGenerator zoneGenerator)
{
this.zoneRepository = zoneRepository;
this.dtoFactory = dtoFactory;
this.zoneGenerator = zoneGenerator;
}
public void Add(ZoneDetailDTO zone)
{
zoneGenerator.Generate(zone);
}
//Do Work
}
public interface IZoneService
{
void Add(ZoneDetailDTO zone);
//Do Methods
}
The generator invoke ohter class, factories:
public class ZoneGenerator
{
private readonly ZoneFactory zoneFactory;
private readonly IZoneRepository zoneRepository;
public ZoneGenerator(ZoneFactory zoneFactory, IZoneRepository zoneRepository)
{
this.zoneFactory = zoneFactory;
this.zoneRepository = zoneRepository;
}
public void Generate(ZoneDetailDTO zoneModel)
{
var zone = zoneFactory.Create(zoneModel);
zoneRepository.Add(zone);
}
}
The Factory:
public class ZoneFactory
{
private readonly ZoneMapFactory zoneMapFactory;
private readonly ZoneScheduleFactory zoneScheduleFactory;
public ZoneFactory(ZoneMapFactory zoneMapFactory,
ZoneScheduleFactory zoneScheduleFactory)
{
this.zoneMapFactory = zoneMapFactory;
this.zoneScheduleFactory = zoneScheduleFactory;
}
public Zone Create(zoneDetailDTO zone)
{
var map = zoneMapFactory.Create(zone.Map.Address, zone.Map.Latitude, zone.Map.Longitude);
var schedule = zoneScheduleFactory.Create(zone.Schedule.StartHour, zone.Schedule.EndHour);
return new Zone(zone.Name,
zone.ProvinceId,
map,
schedule,
zone.Tags);
}
}
And finally my container:
//method in Startup class Asp.Net Core
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_ => Configuration);
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
services.AddMvc();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<DefaultModule>();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return new AutofacServiceProvider(container);
}
public class DefaultModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ZoneService>().As<IZoneService>();
builder.RegisterType<ZoneRepository>().As<IZoneRepository>();
builder.RegisterType<ProvinceService>().As<IProvinceService>();
builder.RegisterType<ProvinceRepository>().As<IProvinceRepository>();
builder.RegisterType<DtoFactory>().As<IDtoFactory>();
}
}
You have missed to add to your Load method the following:
builder.RegisterType<ZoneGenerator>().AsSelf();
builder.RegisterType<ZoneFactory>().AsSelf();
builder.RegisterType<ZoneMapFactory>().AsSelf();
builder.RegisterType<ZoneScheduleFactory>().AsSelf();
I have used singleton pattern a using static property, constructor
public class MyClass
{
private readonly MemoryCacheManager _cacheManager;
private static readonly Lazy<MyClass> _Lazy = new Lazy<MyClass>(() => new MyClass());
public static MyClass Language { get { return _Lazy.Value; } }
private MyClass()
{
_cacheManager = new MemoryCacheManager();
LoadCacheData();
}
// Rest of class
}
I have tried like following using Autofac in global.asax:
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterType<MemoryCacheManager>().SingleInstance();
}
And inside MyClass constructor:
private MyClass(MemoryCacheManager cache)
{
_cacheManager = cache;
LoadCacheData();
}
public string Translate(string language)
{
var translation = _cacheManager.GetValueFromCache();
}
And I want to call this method in Index.cshtml
Previously I did it directly like this:
<h4>#MyClass.Language.Translate("Welcome", Model.Language)</h4>
As I had Language as follow in MyClass:
public static Localizer Language { get { return _Lazy.Value; } }
But now I do not have this property.
How can I call Translate method in Index.cshtml as I do not have static property like previous.
You just need to register MyClass as a SingleInstance with your container:
var builder = new ContainerBuilder();
builder.RegisterType<MyClass>().As<IMyClass>().SingleInstance();
Then inject where ever you need:
public AnotherClass(IMyClass myClass)
{
_myClass = myClass;
}
Although it's probably the cache you want a single instance of. In that case:
builder.RegisterType<MemoryCacheManager>().SingleInstance();
And then:
public MyClass(MemoryCacheManager cache)
{
_cacheManager = cache;
LoadCacheData();
}
EDIT:
The first thing you need to do is set the DependencyResolver in your Application_Start class (you'll need to get the Autofac MVC Integration NuGet package for this):
protected void Application_Start()
{
this.RegisterAutoFac();
// Rest of method
}
private void RegisterAutoFac()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<MyClass>().As<IMyClass>();
builder.RegisterType<MyCache>().As<IMyCache>().SingleInstance();
var container = builder.Build();
// Setup the dependency resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
Then you need to inject MyClass into the constructor of your controller:
public class TestController : Controller
{
private readonly IMyClass _myClass;
public TestController(IMyClass myClass)
{
_myClass = myClass;
}
// Rest of the controller
}
Then when your creating model for you get the data you need from MyClass:
public ActionResult Index()
{
var model = new ExampleModel
{
WelcomeMessage = _myClass.Translate("Welcome")
};
return View(model);
}
And in your view:
<h4>#Model.WelcomeMessage</h4>
I would like to implement CastleWindsor with the MVP pattern, but I keep getting an 'Object Reference Not Set to an Object reference on the Presenter when the repository is called to obtain some data.
This is how I did it and I am wondering if there is anything wrong, so please let me know if you can:
Presenter:
public class CategoryPresenter
{
ICategoryRepository categoryRepository;
ICategoryView categoryView;
public CategoryPresenter(ICategoryView _categoryView, ICategoryRepository _categoryRepository)
{
categoryView = _categoryView;
categoryRepository = _categoryRepository;
}
//public CategoryPresenter(ICategoryView _categoryView) : this (_categoryView, new CategoryRepository())
//{ }
public CategoryPresenter(ICategoryView _view)
{
categoryView = _view;
}
public IEnumerable<object> GetActiveCategories()
{
return categoryRepository.GetActiveCategories();
}
}
IoC Class:
public static class IoC
{
public static IWindsorContainer windsorContainter { get; set; }
}
IoCConfig Class:
class IoCConfig
{
public static IWindsorContainer RegisterCastleWindsorContainer()
{
IWindsorContainer windsorContainer = new WindsorContainer()
.Install(new RepositoryInstaller())
IoC.windsorContainter = windsorContainer;
return windsorContainer;
}
}
Installer Class:
public class RepositoryInstaller: IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Component.For<ICategoryRepository>().ImplementedBy<CategoryRepository>).LifestyleTransient());
}
}
Finally in Global.ascx file I am doing this at App_Start:
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
IoCConfig.RegisterCastleWindsorContainer();
}
With this, the error message is as said above; the error happens at the presenter's method: GetActiveCategories();
As you see at no where in code I invoke the resolve method on the container.
Please let me know if if you have any suggestions.
Thank you.
I have resolved this to the IoC Class
public static T Resolve<T>()
{
try
{
return windsorContainer.Resolve<T>();
}
catch (Exception e)
{
throw e;
}
}
And then add this to the presenter:
ICategoryRepository categoryRepository = IoC.Resolve<ICategoryRepository>();
ICategoryView categoryView = IoC.Resolve<ICategoryView>();