navigation error with Dependency Injection MAUI - c#

I am in the process of transforming my entire application to MVVM and adding Dependency
Injection. for the navigation I have used
Navigation.PushAsync(new date());
it which worked but now its no longer works. do you have some solutions.
inside date.xaml.cs
public partial class date : ContentPage
{
public date(dateViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
private void GoNav(object sender, EventArgs e)
{
Navigation.PopAsync();
}
}
the C# compile error is
CS7036 There is no argument given that corresponds to the required formal parameter 'vm' of 'date.date(dateViewModel)' dateCalculator (net6.0-android), dateCalculator (net6.0-ios), dateCalculator (net6.0-maccatalyst), dateCalculator (net6.0-windows10.0.19041.0) C:\Users\source\repos\dateCalculator\dateCalculator\MainPage.xaml.cs 12

Cause
It is because you remove the default class constructor and create a new one with parameter, so when you call new date() it can't be able to find the constructor without parameter(the default one) , that's why the error comes.
Solution
Remove the new constructor .
Or
Give the value on the parameter when creating the class .
Navigation.PushAsync(new date(YourVm));

Related

ASP.NET MVC 5 Controller custom constructor [duplicate]

I have my own inherited App.Controller from Mvc.Controller which then all of my controllers inherit from. I wrote a provider utilizing an interface and implemented it as MyService and the constructor takes the Server property of Mvc.Controller which is of HttpServerUtilityBase.
However, I instantiate MyService in App.Controller's constructor. The problem is that the Server property of the Controller is null when constructing MyService. I have used public Controller () : base() { } to get the base to be constructed. However, Server remains null.
I would like to avoid Web.HttpContext.Current.Server if possible.
Has any one have a work around for this problem?
Edit: Well, I have implemented tvanfosson's suggestion, and when my app constructs MyService in the property get method, Server is still null.
Edit 2: Nevermind, I was a goof. I had another Controller using Server aswell and did not change that. Case closed.
Use delayed initialization to construct your service.
private MyService service;
public MyService Service
{
get
{
if (this.service == null)
{
this.service = new MyService(this.Server);
}
return this.service;
}
}
Then, your service isn't actually instantiated until it is used in the controller action and by that time the Server property has been set.
I instantiate MyService in App.Controller's constructor.
There's your problem. You need to pass an instance of MyService which has already been constructed into your App.Controller's constructor. Take a look at the Inversion of Control / Dependency Injection patterns, and take a look at some of the libraries which make these patterns easy (see this list).
Why do you need the Server reference? Are you doing stuff like url/html encoding? If so, you could use HttpUtility instead and get rid of the context reference entirely.
This is a very old question, but the subject is still relevant. So, one hint in 2017 that was possibly not available in 2009:
It is true that in the Controller constructor Server is null. But you can use the OnActionExecuting event:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
CurrentServer = Server; // CurrentServer is some instance variable I need later.
}
This works fine for me.
According to this site, if you have this Controller:
public class MyController : Controller
{
private string folderPath;
public MyController()
{
// Throws an error because Server is null
folderPath = Server.MapPath("~/uploads");
// Throws an error because this.ControllerContext is null
folderPath = this.ControllerContext.HttpContext.Server.MapPath("~/uploads");
}
}
Then you want to initialize it this way:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
// now Server has been initialized
folderPath = Server.MapPath("~/uploads");
}

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)

How to use StructureMap to inject service into a ViewModel referenced from XAML?

I may be going about this all wrong, but I thought I was on the right path. I'm building a WPF app for the first time and am still getting used to some of the ways things are done.
My Thinking
My goal is to make my ViewModel class more testable.
The ViewModel depends on a service class and wouldn't be functional without it.
Therefore, it seems to make sense to me to inject the service into the ViewModel so that I can also start mocking it for testing purposes.
What I did
Made a XAML reference to my view model, which is called "UploaderViewModel":
<Window.DataContext>
<viewModels:UploaderViewModel>
</viewModels:UploaderViewModel>
</Window.DataContext>
NOTE: This works fine before I try to do the injection
Set up structure map in my app.xaml.cs file:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
ProcessStartupArguments(eventArgs);
}
Which calls my bootstrapper class:
public static class ContainerBootstrapper
{
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(
x => x.For<IVEDocumentService>().Use<VEDocumentService>()
);
}
}
I then have my UploaderViewModel class take the IVEDocumentService as a Constructor parameter:
public UploaderViewModel(IVEDocumentService documentService)
{
_documentService = documentService;
//other unrelated code below this
}
What's Going Wrong
I'm receiving an XAML error that says there's no default constructor found for the ViewModel, which makes sense, since I have no UploaderViewModel(), only UploaderViewModel(IVEDocumentService documentService).
Question
What's the best way to resolve this?
Should I create an UploaderViewModel() constructor and make StructureMap call UploaderViewModel with an instance of the service, or is there a better way?
Update 1 -- Attempting to Use a Resource Dictionary, StructureMap error.
Based on this SO Answer to a similar question, I tried:
Updating my App_Startup to add an instance of the UploaderViewModel to a resources dictionary:
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
var uploaderViewModel = new UploaderViewModel(new Container().GetInstance<IVEDocumentService>());
Current.Resources["UploaderViewModel"] = uploaderViewModel;
ProcessStartupArguments(eventArgs);
}
Added DataContext="{StaticResource UploaderViewModel}" to my <Window> declaration
Commented out my <viewModels:UploaderViewModel/> declaration
Upon doing this, StructureMap now throw an error:
"StructureMap Exception Code: 202No Default Instance defined for
PluginFamily
I figured it out. I think I wasn't configuring the static resource correctly. The solution, using StructureMap, was to do the following:
App.xaml.cs
void App_Startup(object sender, StartupEventArgs eventArgs)
{
ContainerBootstrapper.BootstrapStructureMap();
//Other logic
}
ContainerBootstrapper Class
public static void BootstrapStructureMap()
{
ObjectFactory.Initialize(x => x.For<IVEDocumentService>().Use<VEDocumentService>());
}
MainWindow.xaml.cs
I moved the resource creation into this class:
public MainWindow()
{
var uploaderViewModel = new UploaderViewModel(ObjectFactory.GetInstance<IVEDocumentService>());
this.Resources["UploaderViewModel"] = uploaderViewModel;
InitializeComponent();
}
MainWindow.xaml
On the <Window> definition, I set DataContext="{StaticResource UploaderViewModel}".
Works like a charm. I believe it was a combination of not binding the resources at the proper scope (window).

TabHost pass Parameter in CreateIntentFor<>

After downloading the newest Version of mvvmcross (V3) I had some work to do, to upgrade some of my projects to the new state. Last thing I'm not able to fulfill is to pass a parameter to the viewmodel from the tabhost. In the older versions it worked fine (but it was different) and now I got an error.
But First here the Code (Line 19 makes trouble (watch comment in code), Line 18 works but only without Parameters):
[Activity]
public class MainActivity : MvxTabActivity
{
public new MainViewModel ViewModel
{
get { return (MainViewModel)base.ViewModel; }
set { base.ViewModel = value; }
}
protected override void OnViewModelSet()
{
SetContentView(Resource.Layout.Main);
TabHost.TabSpec spec;
Intent intent;
spec = TabHost.NewTabSpec("listeaktiv");
spec.SetIndicator(App.indicatorActive, Resources.GetDrawable(Resource.Drawable.green));
//spec.SetContent(this.CreateIntentFor(ViewModel.ListViewModel)); -> It works (But only without Parameters! How could I pass them here?)
spec.SetContent(this.CreateIntentFor<ListViewModel>(new { parameter = App.indicatorActive })); //Exception (on the next Line)
TabHost.AddTab(spec);
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
}
(App.indicatorActive is the Parameter I want to pass, its in the App.cs: (public static string indicatorActive = "Active";)
My ListViewModel looks like that:
public class ListViewModel : BaseViewModel
{
public ListViewModel(string parameter)
{
}
}
Error:
Unhandled Exception:
Cirrious.CrossCore.Exceptions.MvxException: Failed to load ViewModel for type
INApplikationsMonitor.Core.ViewModels.ListViewModel from locator MvxDefaultViewModelLocator
My guess is that this is just because you are using the old ViewModel lifecycle.
In v3:
the ViewModel constructor parameters are used for IoC - for Dependency Injection of services.
for passing parameters you need to instead use an Init method within the ViewModel
For more on this, see: http://slodge.blogspot.co.uk/2013/03/v3-new-viewmodel-lifecycle.html :
The default ViewModelLocator in v3 builds new ViewModel instances using a 4-step process - CIRS:
Construction - using IoC for Dependency Injection
Init() - initialisation of navigation parameters
ReloadState() - rehydration after tombstoning
Start() - called when initialisation and rehydration are complete

AOP with Ninject Interception, Castle DynamicProxy and WPF window: Can't find XAML resource in DynamicProxy of window

In our real world application we defined an attribute that is used to enable logging in methods or classes (the usual AOP use case). When we apply this attribute to a WPF window class, objects of this class can't be created by Ninject. Here is a minimal example to reproduce the issue:
dummy interceptor for logging:
public class MyInterceptor: IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine("Calling {0} at {1}", invocation.Request.Method.Name, DateTime.Now);
invocation.Proceed();
}
}
the corresponding attribute:
public class MyAttribute: InterceptAttribute
{
public override IInterceptor CreateInterceptor(IProxyRequest request)
{
return new MyInterceptor();
}
}
the window class (completely empty, only the automatically generated empty grid is inside):
[My]
public partial class MainWindow: Window
{
public MainWindow()
{
InitializeComponent();
}
}
and finally the app startup code where the object is requested:
public partial class App: Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
var kernel = new StandardKernel(new NinjectSettings() { LoadExtensions = false }, new DynamicProxyModule());
var window = kernel.Get<MainWindow>();
window.ShowDialog();
}
}
When requesting the window via kernel.Get<MainWindow>(); an TargetInvocationException is thrown with an inner exception telling me that Castle.Proxies.MainWindowProxy doesn't have a resource specified by URI "/NinjectInterceptionWPF;component/mainwindow.xaml" where NinjectInterceptionWPF is our assembly's short name.
When we look at the automatically created InitializeComponent of MainWindow we can see that an URI is created to address the XAML code, which seems to be missing for the proxy:
System.Uri resourceLocater = new System.Uri("/NinjectInterceptionWPF;component/mainwindow.xaml", System.UriKind.Relative);
#line 1 "..\..\..\MainWindow.xaml"
System.Windows.Application.LoadComponent(this, resourceLocater);
I already played around a bit and tried to use an absolute URI but LoadComponent only accepts relative ones.
Some internet searching shows that a lot of people use Ninject Interception and DynmaicProxy for WPF binding (INotifyPropertyChanged), so I think in general it should be possible to build a proxy of a WPF window.
But how?
The interception extension of Ninject created a new dynamic assembly. This means you won't be able to load resources with a relative path. But the question here is if you really want to create a dynamic proxy for the view. Usually you should do this on your ViewModel instead.

Categories