Can Assets/Resoures be shared between iOS + Droid projects? - c#

I have a Xamarin based Visual Studio 2015 project that has the following structure.
MyApp (Portable)
MyApp.Droid
MyApp.iOS
Currently I include assets / resources in the MyApp.Droid project and the MyApp.iOS project.
So for example myhtml.html is duplicated as both MyApp.Droid/Asserts/myhtml.html and MyApp.iOS/Resources/myhtml.html
Are there any ways I can avoid this duplication?

Assuming you are using Xamarin Forms, yes, you can use .net resource files.
You can create a folder in the portable project, add there the content files and set the Compilation Action to Embedded Resource for all of those, then you can use the .net provided mechanism to acces the resources, per example (in this example I assume the code is being executed in a class contained in the portable project and the files are stored in a folder called ResourceFiles):
var htmlFile = this.GetType().GetTypeInfo().Assembly.GetManifestResourceStream("MyNamespace.ResourceFiles.myhtml.html");
In this way you have the stream with the file's content.
Also, as a hint, you can load these files from XAML using a custom markup extension, per example, here is a markup extension to load images from .net resources:
[ContentProperty ("Source")]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue (IServiceProvider serviceProvider)
{
if (Source == null)
return null;
var imageSource = new StreamImageSource { Stream = async (ct) => this.GetType().GetTypeInfo().Assembly.GetManifestResourceStream(Source) };
return imageSource;
}
}
Then, to use this extension in XAML you will do (local is the XAML namespace definition of your own namespace):
<Image Source="{local:ImageResource MyNamespace.ResourceFiles.MyImage.png}" />

Related

How can a Xamarin.Android application access files that have the Copy to Output Directory set to CopyAlways?

I am in the process of migrating a regular .net framework legacy library into a .net standard library. As shown in the picture below, the legacy library used to have a file that is always copied to the Output folder:
In that library, there is a legacy class that test the existence of the folder before accessing the file on the disk, like this:
namespace Library
{
public class LegacyClass
{
public void AccessFile()
{
string path = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
bool directoryExists = Directory.Exists(Path.Combine(path, "Files"));
// It does exist in the Console app. It does not in the Android app.
// ... And then access the file.
}
}
}
While it was working with a Console app, it is not working in a blank Xamarin application:
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//
var legacyClass = new LegacyClass();
legacyClass.AccessFile();
}
The LegacyClass is not able to find the Files folder:
Question
How can a Xamarin.Android application access files that have the Copy to Output Directory set to CopyAlways?
You can't do that, but you can achieve your goal using embedded resources easily.
https://developer.xamarin.com/samples/mobile/EmbeddedResources/
Instead, you've to add file to your csproj, then you can set its build action to Embedded Resource in the properties of that file. (Select file in solution explorer and press F4).
At runtime, you can use the following that returns a Stream to your file:
Assembly.Load("MyProjectWhichContainsTheFile").GetManifestResourceStream("MyFile.txt")

add image in another folder instead of drawable in xamarin cross-platforms

I am able to display image from Drawable folder by using
<image source="live.png"/>
But i don't know how to get image from other folder i create in Resource
Can somebody help me?
Android is very picky about where you can put images, so your best bet is to store your images in the common project. In this example I assume a standard Xamarin.Forms solution with the following projects: Foo, Foo.Android, Foo.IOS and Foo.UWP. Yours will obviously have different names, so you'll have to substitute the Foos...
All the following code will go into the common code project, Foo.
First, create a new folder called Extensions (just to keep your code tidy) and add the following class to it:
[ContentProperty(nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
var imageSource = ImageSource.FromResource(Source);
return imageSource;
}
}
Now add the namespace for this class to your markup:
xmlns:extensions="clr-namespace:Foo.Extensions"
Next, create a folder for your images, again in your common project, NOT in your Android project. You can create subfolders as well. Add your images and make sure that the build action for each image is set to Embedded Resource.
Now you can reference those images in your XAML like this:
<Image Source="{extensions:ImageResource Foo.Images.Subfolder.Bar.png}">
Note that you need to supply the full path of the image, including the project name (Foo in this case) and that folders are separated by dots, not slashes.

Xamarin forms shared project with images for Android and iOS

I am trying to upload an image inside the Shared project in my solution so that the Android and iOS project can use it normally via Xaml. Not working, I do not know why. Can anyone explain why it is not working?
Extension:
[ContentProperty("Source")]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null)
{
return null;
}
// Do your translation lookup here, using whatever method you require
var imageSource = ImageSource.FromResource(Source);
return imageSource;
}
}
Xaml:
<Image Source="{extensions:ImageResource MyProject.Assets.fechar.png}" HorizontalOptions="Start" HeightRequest="16" Focused="Image_Focused" BackgroundColor="Red" WidthRequest="15" />
Putting a breakpoint in the Extension it usually goes there once for each item in my list.
I've also tried inserting a hardcoded binding by codebehind only for testing and it also does not work using ImageSource.FromResource.
The image file is as EmbbedResource as the documentation indicates.
Don't use extension. There is much easier way.
According to Xamarin "Currently there is no implicit conversion for resource identifiers, you must use ImageSource.FromResource" and "Because there is no built-in type converter from string to ResourceImageSource, these types of images cannot be natively loaded by Xaml". So you cannot specify in xaml that your image is shared but you can use code behind to bind a shared image.
In xaml:
<Image Source="{Binding Image}" HorizontalOptions="Start" WidthRequest="80"/>
Then in code behind
public ImageSource Image
{
get
{
return ImageSource.FromResource("ButtonRendererDemo.Resources.icon1.png"); //from PCL
}
}
below is the project structure
//////////////////////////////////////////////////////////////////
Tested with shared project. Below is the structure
The key is to add reference of your shared project to your PCL where you are calling FromResource. The shared project gets injected into PCL
Then in my project structure it is
public ImageSource Image
{
get
{
//return ImageSource.FromResource("ButtonRendererDemo.Resources.icon1.png"); //from PCL
return ImageSource.FromResource("ButtonRendererDemo.Pictures.icon1.png"); //from Shared
}
}
//////////////////////////////////////////////////////////////
If nothing works for you use the follow code to find your resource. In the file where you call FromResource add using System.Reflection; then add the follow code
var assembly = typeof(App).GetTypeInfo().Assembly;
foreach (var res in assembly.GetManifestResourceNames())
{
System.Diagnostics.Debug.WriteLine("found resource: " + res);
}
You should see in Output window something like
found resource: someNamespace.Pictures.icon1.png - That's your resource you need to use.
You didn't answer my question about your project structure. Then I could give you an exact answer. I suspect that your path to image is "MyProject.Droid.Assets.fechar.png"
FromResource is referring to the Android or iOS project not the shared one. The images need to be in the appropriate folder of the platform specific project.

Resource files (resx) does not respect Custom Tool Namespace

In the resx properties, I changed the Custom Tool Namespace from DefaultNamespace to MyNamespace.Language and the following code is generated:
namespace MyNamespace.Language
{
public class CommentResources
{
public static global::System.Resources.ResourceManager ResourceManager {
get {
//removed code...
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DefaultNamespace.CommentResources", typeof(CommentResources).Assembly);
}
}
As you can see, only the class namespace is changed, but not the namespace passed in the ResourceManager constructor and because of that, when I instanciate ResourceManager(typeof(CommentResources)) and try to access a key, it throws MissingManifestResourceException, for example:
var manager = new ResourceManager(typeof(CommentResources));
var resource = manager.GetString("myKey");
How can I truly changed the namespace?
EDIT:
Take a look at my solution below. Whenever I create a resx file within Enviroment folder, it creates a unwanted namespace. That's what's I'm trying to avoid
I recently stumbled to the same issue.
It seems that Visual Studio 2017 generates code that creates ResourceManager from RootNamespace.SubFolder.ResourcesFileName instead of CustomToolNamespace.ResourcesFileName.
Since the code is created from the Visual Studio's Single-File Generator tool called either ResXFileCodeGenerator (for internal class) or PublicResXFileCodeGenerator (for public class) that internally uses StronglyTypedResourceBuilder class, there is no way to customize its behavior, other than implementing your own Single-File Generator as a Visual Studio extension and using it as a Generator for the EmbeddedResource.
Fortunately, there is a simpler workaround. In .csproj file, under EmbeddedResource tag, specify LogicalName tag with text value of RootNamespace.SubFolder.ResourcesFileName.resources.
In your specific case, it would look like this:
<LogicalName>DefaultNamespace.CommentResources.resources</LogicalName>

Visual Studio MVC template creation issue

I am attempting to create a custom ASP.NET MVC4 template. I start from the Basic MVC4 template, make my modifications, and then use the "Export Template" wizard to create the template zip file. Right now (almost) everything is working smoothly. When I use the template to create a new MVC application, it recreates all of my settings the way I want them except one. For some reason, it changes the project properties for my web application to have a Start Action of "Current Page" instead of "Specific Page" (like it was in the original template and like it is in my template). This setting is in the project properties under the Web tab. Here is what it is set to in my template application (before I generate the actual template zip file):
And here is what it is like when I create a new project using that template:
How do I modify my template to set this setting properly (or more accurately, how do I force it to remember what I set initially)?
Edit: answer revised due to misunderstanding of both original request and behavior of previously proposed solution.
To set the Start Action of a custom MVC project template, you'll need to create a dll with a class that implements the Microsoft.VisualStudio.TemplateWizard.IWizard interface. To use the wizard dll, you'll either need to copy it to Visual Studio's probing path, which is (VS2010 Install Dir)\Common7\IDE, (VS2010 Install Dir)\Common7\IDE\PrivateAssemblies, or (VS2010 Install Dir)\Common7\IDE\PublicAssemblies. If you don't put the compiled dll in one of those directories, you'll need to strong name and sign the dll and add it to the GAC and get the publickeytoken of the dll and add it to the Assembly element in the vstemplate file.
In testing the following code I copied the dll to (VS2010 Install Dir)\Common7\IDE\PrivateAssemblies, so the dll is not signed.
Wizard code
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using EnvDTE;
using Microsoft.VisualStudio.TemplateWizard;
namespace WarrenG.StartAction {
public class Wizard : IWizard {
private readonly Dictionary<string, object> data = new Dictionary<string, object>();
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary,
WizardRunKind runKind, object[] customParams) {
if (replacementsDictionary.ContainsKey("$wizarddata$")) {
string xml = replacementsDictionary["$wizarddata$"];
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
foreach (XmlNode node in doc.ChildNodes) {
data.Add(node.Name, node.InnerText);
}
}
}
public bool ShouldAddProjectItem(string filePath) {
return true;
}
public void RunFinished() {
}
public void BeforeOpeningFile(ProjectItem projectItem) {
}
public void ProjectItemFinishedGenerating(ProjectItem projectItem) {
}
public void ProjectFinishedGenerating(Project project) {
if (data.ContainsKey("WebApplication.DebugStartAction")) {
project.Properties.Item("WebApplication.DebugStartAction").Value =
data["WebApplication.DebugStartAction"];
} else {
project.Properties.Item("WebApplication.DebugStartAction").Value = 1;
}
}
}
}
Add wizard specific elements to vstemplate file of custom MVC project template
<VSTemplate Version="3.0.0" xmlns="http://schemas.microsoft.com/developer/vstemplate/2005" Type="Project">
<TemplateContent>
<!-- various template content -->
</TemplateContent>
<!-- add the following -->
<WizardExtension>
<Assembly>WarrenG.StartAction, Version=1.0.0.0, Culture=Neutral, PublicKeyToken=null</Assembly>
<FullClassName>WarrenG.StartAction.Wizard</FullClassName>
</WizardExtension>
<WizardData>
<WebApplication.DebugStartAction>1</WebApplication.DebugStartAction>
</WizardData>
</VSTemplate>
The start actions on the project page appear to be numbers 0 through 4, following their display order. A value of 1 corresponds with Specific Page.
Unfortunately, or fortunately, depending on the side of the coin you're on...
Like the "Startup Project" setting, that setting is NOT part of the project file or the template file that's generated. It is stored in the "SUO" or Solution User Options, file. The SUO is not included by the template generator.
Some background on the SUO file: http://msdn.microsoft.com/en-us/library/bb165909(v=vs.80).aspx

Categories

Resources