Dependency Injection in XAML (WPF) - xaml

I am creating a new WPF project and we use Microsoft Unity as DI.
I am having a user control which is calling a 3rd party service.
So now how to inject dependency from the main window XAML for the usercontrol.

You can use the service locator pattern. I use it with Unity as a DI.
internal class ServiceLocator
{
[...]
public MainViewModel Main { get { return container.Resolve<MainViewModel>(); } }
}
You can intantiate your class the way you want (DI or not, the class initializes the DI etc...).
In your App.xaml
<Application.Resources>
<vm:ServiceLocator x:Key="Locator"/>
</Application.Resources>
And now, you can set your datacontext
DataContext="{Binding Main, Source={StaticResource Locator}}"
Edit:
I found another way of doing it (among other):
Take a look at this article. In the command, you can resolve your viewmodel as you like.

Related

ViewComponent in external assembly cannot be found

I am using the latest VS.2017 updates and templates for an MVC .NET Core web application. I decided I wanted ViewComponents in an external assembly since I read several posts that indicated it was not possible without odd tricks.
I have my main web application and then I created a .NET Framework class library named MySite.Components which is the "external assembly". In it I installed the ViewFeatures NuGet. I created my View component CSHTML in its /Views/Shared/Components/GoogleAdsense/Default.cshtml.
I noticed that my CSPROJ already has the GoogleAdSense as an embedded resource:
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
<EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
</ItemGroup>
The view component is actually quite simple:
namespace MySite.Components.ViewComponents {
[ViewComponent(Name = "GoogleAdsense")]
public class GoogleAdsense : ViewComponent {
public async Task<IViewComponentResult> InvokeAsync(string adSlot, string clientId, string adStyle = "")
{
var model = await GetConfigAsync(adSlot, clientId, adStyle);
return View(model);
}
private Task<GoogleAdUnitCompModel> GetConfigAsync(string adSlot, string clientId, string adStyle)
{
GoogleAdUnitCompModel model = new GoogleAdUnitCompModel
{
ClientId = clientId, // apparently we can't access App_Data because there is no AppDomain in .NET core
SlotNr = adSlot,
Style = adStyle
};
return Task.FromResult(model);
}
}
}
Then in the main project (the ASP.NET Core web application) I installed the File Provider NuGet and modified my Startup:
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(
typeof(MySite.Components.ViewComponents.GoogleAdsense).GetTypeInfo().Assembly,
"MySite.Components.ViewComponents"
));
});
Then I try to use the view component in a view like this:
#using MySite.Components.ViewComponents
:
#Component.InvokeAsync(nameof(GoogleAdsense), new { adSlot = "2700000000", clientId = "ca-pub-0000000000000000", adStyle="" })
And I get an error saying
*InvalidOperationException: A view component named 'GoogleAdsense' could not be found.*
Also tried using the notation without nameof() that uses a generic parameter for InvokeAsync but that fails too but with
*"Argument 1: cannot convert from 'method group' to 'object'"*
And using the TagHelper form simply renders it as an unrecognized HTML:
<vc:GoogleAdsense adSlot = "2700000000" clientId = "ca-pub-0000000000000000"></vc:GoogleAdsense>
Finally, on the Main Assembly (the actual web application) I used the GetManifestResourceNames() on the external assembly type to verify it was embedded and the returned list had it listed as:
[0] = "MySite.Components.Views.Shared.Components.GoogleAdsense.Default.cshtml"
I did a lot of trial-and-error and was finally able to get this working. There's a number of guides on this, but they're all for .NET Core 1.0, and I also found they did not work when using a DLL reference from another solution.
Let's talk about component name first. The component name is determined either by convention or attribute. To name by convention, the class name must end in "ViewComponent", and then the component name will be everything prior to "ViewComponent" (just like Controller names work). If you just decorate the class with [ViewComponent], the component name will explicitly be the class name. You can also directly set the name to something else with the attribute's Name parameter.
All three of these examples produce a component name of "GoogleAdsense".
public class GoogleAdsenseViewComponent : ViewComponent { }
[ViewComponent]
public class GoogleAdsense : ViewComponent { }
[ViewComponent(Name = "GoogleAdsense")]
public class Foo: ViewComponent { }
After that, be sure your views are in the proper folder structure.
├── Views
│ ├── Shared
│ │ ├── Components
│ │ │ ├── GoogleAdsense <--component name
│ │ │ │ ├── Default.cshtml
Then, the Views must all be included as embedded resources. Right-click > Properties on the view and set the Build Action to "Embedded resource". You can also do this manually in the .csproj (and take advantage of globbing if you have a lot of Views).
<ItemGroup>
<EmbeddedResource Include="Views\Shared\Components\GoogleAdsense\Default.cshtml" />
</ItemGroup>
That's it for the source project. Make note that you must do a build for any changes to your views to show up, since they are being included in the DLL. This seems obvious, but it's a change from how you normally interact with views.
Now to the consuming project. In ConfigureServices in Startup.cs, you must add your component's assembly as both an MVC ApplicationPart and as an EmbeddedFileProvider. The EmbeddedFileProvider gives access to the views embedded in the assembly, and the ApplicationPart sets up MVC to include it in its search paths.
var myAssembly = typeof(My.External.Project.GoogleAdsenseViewComponent).Assembly;
services.AddMvc().AddApplicationPart(myAssembly);
services.Configure<RazorViewEngineOptions>(options =>
{
options.FileProviders.Add(new EmbeddedFileProvider(myAssembly, "My.External.Project"));
});
If you have multiple ViewComponents in that assembly, this will suffice for all of them. You can optionally provide a base namespace to EmbeddedFileProvider. I have found times when it was necessary and times when it was not, so it is best to just provide it. This namespace should be the Default Namespace property of your project (Properties -> Application -> Default Namespace).
Finally, to invoke the ViewComponent, use the component name. Remember that the component name may differ from the class name. Unless you used [ViewComponent] to set the component name to be the class name, you cannot use nameof.
#await Component.InvokeAsync("GoogleAdsense")
I was able to get a ViewComponent working from an external solution by generating and installing a NuGet package from the "external" assembly into the consuming solution with no problem. I had originally tried to add a reference to the dll without creating my own NuGet package and it did not work.
I'd recommend trying the NuGet package first. If it still doesn't work, can you post both projects so I can help debug?

Xamarin Forms from Windows Phone 8.1(RT) - xaml exceptions

I created app with Xamarin Forms for Windows Phone 8.1(RT). I updated XF to last version 2.3.4.231. My app in run to:
-windows phone 8.1 device
-windows phone 8.1 emulator
-mobile emulator 10.0.14393(x86)
It work is ok.
But when I running app to Windows 10 device(arm) I have many exception. I try different start pages and get mane exceptions(from xaml):
-Cannot assign property \"ColumnDefinitions\": Property does not exists, or is not assignable, or mismatching type between value and property"
-StaticResource not found for key ...
-An item with the same key has already been added
All these errors are related to xaml.
I do not use XamlCompilationOptions.Compile.
Last working version XF for my app 2.3.2.127
This small example:
I changed start page. I have exception:
-StaticResource not found for key StandardPadding
This is part of my page:
<StackLayout Padding="{StaticResource StandardPadding}">
My resource in App.xaml:
<Application.Resources>
<ResourceDictionary>
<Thickness x:Key="StandardPadding">16</Thickness>
</ResourceDictionary>
</Application.Resources>
I solved this problem. The error was in my xaml code. Xamarin Forms 2.3 not supported Windows OnPlatform in xaml. So I used this extension:
public class XOnPlatform<T> : OnPlatform<T>
{
public T Windows { get; set; }
public static implicit operator T(XOnPlatform<T> onPlatform)
{
if (Device.OS == TargetPlatform.Windows)
{
return onPlatform.Windows;
}
return (OnPlatform<T>)onPlatform;
}
}
I used this class in my xaml code. But in XF 2.3.4 Device.OS is obsolete. I removed XOnPlatform from my xaml code. I use OnPlatform in my xaml code.This is work. Bingo :)

UWP + MvvmLight - ViewModelLocator defined as App.xaml resource is returning Access Denied error in Design Mode

I'm building UWP app with MVVMLight and I have problem with my ViewModelLocator in Design Mode.
I created ViewModelLocator:
public class ViewModelLocator
{
public MainPageViewModel Main =>ServiceLocator.Current.GetInstance<MainPageViewModel>();
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainPageViewModel>();
if (ViewModelBase.IsInDesignModeStatic)
{
SimpleIoc.Default.Register<IMainPageDataService, DesignMainPageDataService>();
}
else
{
SimpleIoc.Default.Register<IMainPageDataService, MainPageDataService>();
}
}
}
And then I declared it as App resource:
<Application.Resources>
<ResourceDictionary>
<viewModels:ViewModelLocator x:Key="Locator"></viewModels:ViewModelLocator>
</ResourceDictionary>
</Application.Resources>
It should work now and I should have possibility to define MainPageViewModel as MainPage DataContext but Intellisense is underlining
<viewModels:ViewModelLocator x:Key="Locator"></viewModels:ViewModelLocator>
with error
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
I noticed that when I'm not using ViewModelBase.IsInDesignModeStatic property it works properly and I can see design data at MainPage.
Something like this works:
public class ViewModelLocator
{
public MainPageViewModel Main =>ServiceLocator.Current.GetInstance<MainPageViewModel>();
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainPageViewModel>();
SimpleIoc.Default.Register<IMainPageDataService, DesignMainPageDataService>();
}
}
Have you any ideas to resolve this weird problem?
Well... it was something wrong with my environment. I checked it on another machine and it works flawless.
I ran into this same issue and finally fixed it by using the native UWP Windows.ApplicationModel.DesignMode.DesignModeEnabled and now all is working well.
Additionally, I am in the process of switching to Prism, unclear if this is a VS/Blend issue, or an MVVMLight issue, but slowing finding more items I find easier to use and more robust about Prism 6, such as its Validation for example.

Ninject intercept throwing error. Dynamic proxy

I have a class which I use to bootstrap.
as part of the object creation I use by convention to bind to interfaces.
All works OK until I try to add an interceptor.
public class ContainerBootstrapper : IDisposable
{
StandardKernel _c;
public ContainerBootstrapper()
{
_c =new StandardKernel();
_c.Bind(b => b.FromAssembliesMatching("Facade*.*").SelectAllClasses().BindDefaultInterfaces());
_c.Bind(b => b.FromAssembliesMatching("Object*.*").SelectAllClasses().BindDefaultInterfaces());
_c.Bind(b => b.FromAssembliesMatching("Logger*.*").SelectAllClasses().BindDefaultInterfaces());
//even using the built in ActionInterceptor like this:
_c.Intercept(c => true)
.With(new ActionInterceptor(invocation =>
Console.Write(invocation.Request.Method.Name)));
When this line is hit, I get an error - Error loading Ninject component IAdviceFactory
No such component has been registered in the kernel's component container.
Suggestions:
1) If you have created a custom subclass for KernelBase, ensure that you have properly
implemented the AddComponents() method.
2) Ensure that you have not removed the component from the container via a call to RemoveAll().
3) Ensure you have not accidentally created more than one kernel.
I have at the top:
using Ninject.Extensions.Conventions;
using Ninject.Extensions.Interception.Injection.Dynamic;
using Ninject.Extensions.Interception.Infrastructure.Language;
using Ninject.Extensions.Interception;
and used NuGet for packages. Tried both Dynamic Proxies and LinFu. Both gave same error.
Anyone have any ideas to try?
Thanks in advance.
Turns out that even though I had a reference to the project doing the bootstrapping and I thought all my dlls for ninject where being copied over automatically this was not the case. After moving them manually it worked.

Using Sugar ORM when also using singleton application class

I have extended the Android.Application class so that I can keep state (using the application as the singleton).
However, I now want to use the Sugar ORM library to simplify my DB access, but the Sugar docs (http://satyan.github.io/sugar/getting-started.html) require that I specify 'SugarApp' as my application class in AndroidManifest.xml.
Which clashes with my Application class that extends Android Application.
Is there an alternative approach?
From the Sugar docs:
E.g. by changing the android:name attribute of the application tag.
<application android:label="#string/app_name" android:icon="#drawable/icon"
android:name="com.orm.SugarApp">
.
.
<meta-data android:name="DATABASE" android:value="sugar_example.db" />
<meta-data android:name="VERSION" android:value="2" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.example" />
.
.
</application>
Answering my own question -tut tut ;-)
It's simple, I just changed my Custom App class to extend SugarApp instead of Android.Application, as SugarApp itself extends android app!
If you take a look at SugarApp class, you will find that it extends Application class overriding the onCreate() & onTerminate() methods doing its scaffolding:
#Override
public void onCreate() {
super.onCreate();
SugarContext.init(this);
}
#Override
public void onTerminate() {
super.onTerminate();
SugarContext.terminate();
}
The only thing you have to do if you want to keep your base application class is to add those method calls to your custom class.
In this way, you can maintain your custom application class instead of extending SugarApp class.