I've a text file and I want to reach it from a shared code project. I want it to work for Android, iOS and Windows Phone. What would be the best approach and where should I put the text file?
You can use the following plugin to reach it: https://github.com/dsplaisted/PCLStorage
For more information about where to put it and build settings take a look at this: https://developer.xamarin.com/guides/xamarin-forms/working-with/files/
Since a shared project gets compiled into the native project you will need to place it there (copy the file to the Android, iOS & Windows Phone project). Now you have to access it from your shared project by using reflection like this:
var assembly = GetType().GetTypeInfo().Assembly;
using (var stream = assembly.GetManifestResourceStream("YourNativeAssembly.FolderYouPutTheFileIn.filename.txt"))
using (var reader = new StreamReader(stream))
{
string contents = reader.ReadToEnd();
}
Do note that the file you just added should be a EmbeddedResource!
If you want to target multiple platforms you would probably have to change the 'YourNativeAssembly' based on the platform (or name every native assembly the same). To do that see this documentation.
Related
I am trying to implement a very simple function in NET MAUI, however I am not able to do it. I have an excel file that I want to deploy as a resource on the target device and read later in the application.
In Windows App I can do this very easily by setting the Excel file as content and activating copy.
How does this work on Android? In some posts I have read that one should proceed as follows, yet it does not work:
you open .csproj and define there.
<ItemGroup>
<MauiAsset Include="Resources\Assets\*" />
</ItemGroup>
then create a new folder Assets in the project under Platforms -> Android -> Resources and place the Excel file Info.xlsx there.
set the build process for excel file as MauiAsset. I have also activated the copy here.
Now I read the Excel file in the program via the OpenXml library:
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(fileName, false))
I set the location of the file using:
string mainDir = FileSystem.Current.AppDataDirectory;
var fullpath = Path.Combine(mainDir, #"Info.xlsx");
When running, the debug reports that the file does not exist. If you check the data -> com.company... folder on the Android device, you will see only cash and no other folder!
By the way I tried something before and found out that you can't create folders on an Android device via MAUI, so I wasn't very surprised here when I only saw cash folders.
Am I doing something fundamentally wrong or is it MAUI bug? You read in some places already that MAUI is not redy for use and even worse that even basic things do not work.
I hope someone has an idea what is going on here.
Thanks
pcsasa
EDIT:
So, I've been working on the problem of reading a simple text file for a couple of hours now, and it's been without result.
In summary:
I noticed that Visual Studio also has a Resources folder in the project and a Raw folder under it. I now also have the same structure under Platforms -> Android then Resources -> Raw because I don't know where the app is looking.
in the folder I then placed an usual text file and set build to MauiAsset
then I used from MAUI doc the code to read "Bundled files":
public async Task<string> ReadTextFile(string filePath)
{
using Stream fileStream = await FileSystem.Current.OpenAppPackageFileAsync(filePath);
using StreamReader reader = new StreamReader(fileStream);
return await reader.ReadToEndAsync();
}
once I specified plain text file as filePath and when that didn't work, I also supplied the path:
var fullpath = Path.Combine(#"Resources/Raw", #"AboutAssets.txt");
...but that doesn't work either!
The thing that surprises me the most is that there is no error message, but the string set is equal to "" (so not even null).
I really can't imagine that no one has included a file in the project until now to read it out in the application (e.g. image, settings, texts, Excel, etc.)? I wonder because there is really nothing in the MAUI documentation that describes how to read a file on an Android device except this "File system helpers" post. You can not find even in forums this question that I asked?
Is the mistake somewhere with me and I ask stupid questions, or do I try here something what does not go at all, because MAUI has a fundamental error in itself?
If my intention cannot be implemented in MAUI (i.e. simply include the file in the project and read it out on the target device), then that is a criterion for exclusion from using MAUI.
Thanks
pcsasa
To read a plain text file in Andriod, you could drag a TextFile.txt to Project/Resources/Raw folder and set its Build Action to MauiAsset.
You could check in the *.csproj file, please remove the following code if it exists
<ItemGroup>
<None Remove="Resources\Raw\TextFile.txt" />
</ItemGroup>
<ItemGroup>
<BundleResource Include="Resources\Raw\TextFile.txt" />
</ItemGroup>
Then you could try reading the string in text file like this:
using var stream = await FileSystem.OpenAppPackageFileAsync("FileText.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
Console.WriteLine(contents);
Hope it works for you.
Thanks Liqun Shen-MSFT
Since I have lost a lot of time and there are probably still people who have or will have the same problem, here again resumed how to include a resource (text or Excel) via NET MAUI and read out on the target device (eg an Android Phone) and use in the program:
if one, like me, has entered an entry to the resource in cproj, delete it !
in the project under Project/Resources/Raw insert the required resource (I tried it only with text and Excel) and set Build Action to MauiAsset.
the readout is done with the code :
using var stream = await FileSystem.OpenAppPackageFileAsync("FileText.txt");
using var reader = new StreamReader(stream);
var contents = reader.ReadToEnd();
Console.WriteLine(contents);
...or if you want to read an Excel file via OpenXml, then you pass the stream to the function:
using (SpreadsheetDocument spreadSheetDocument =
SpreadsheetDocument.Open(stream, false))
So this worked very well in the Android emulator as well as on a physical device (Android Phone). If it works on the iPhone I couldn't try it, but I would be very happy if someone could check it and write something here.
Thanks for the help.
In a separate project I have a de-serialized xml file with strings that I want to use in my WPF application, these strings are also used within a different project in the same solution, so I can't just move the strings over to the project holding the wpf application.
The program is structured like this:
Project A references B and C
WPF application with event handlers
Project B references C
GUI logic in F#
Project C
XML resource file and de-serializer (written in F#)
Is there a way for me to make a resource or resource dictionary based on the objects from the deserialized xml file? or can I reference the strings stored in the xml file directly?
I would suggest you to use the built-in .resx file, which is included by default in freshly created WPF application. (You can also add it at later time using Project -> Add New Item... -> Resource File.) Just be sure to set the access modifier to "Public" and you will be able to use the strings inside other projects that references the current one:
You will be able to access strings (and other resources as well!) through a strongly-typed, automatically generated wrapper class:
MessageBox.Show(WpfApplication1.Properties.Resources.String1);
You can also set properties using XAML to the strings stored in this file, using the x:Static markup extension, as desribed in (for example) this answer:
xmlns:resx="clr-namespace:WpfApplication1.Properties;assembly=WpfApplication1"
Title="{x:Static resx:Resources.String1}"
Additionally you'll get nice, Visual Studio built-in, UI for editing the file (also visible in the picture).
From what it seems your projects that will use the common resource strings will be implemented using WPF technology. In this case you could change a little the format of your xml and have directly a xaml file containing the serialized form of a ResourceDictionary. After that you can just deserialize your file and you have a ResourceDictionary. Serialization and deserialization can be done using the XamlWriter and XamlReader.
Code example:
var pc = new ParserContext
{
BaseUri = new Uri(runtimeResourcesDirectory , UriKind.Absolute)
};
...
using (Stream s = File.Open(resourceDictionaryFile, FileMode.Open, FileAccess.Read))
{
try
{
var resourceDictionary = XamlReader.Load(s, pc) as ResourceDictionary;
if (resourceDictionary != null)
{
Resources.MergedDictionaries.Add(resourceDictionary);
}
}
catch
{
MessageBox.Show("Invalid xaml: " + resourceDictionaryFile);
}
}
}
The tooling in F# project is not so good, so what I have suggested in my previous answer won't work.
In C# projects, there is custom tool named "ResXFileCodeGenerator" that is run (just before the build) over the .resx file and generates the C# wrapper class. This is obviosly not available in F# projects. You have to workarounds though.
The easiest approach that comes to my mind - that is if you want to use the strongly typed wrapper generated from the C# custom tool - is to add new C# class library to your solution, which will contain the resx file and will generate the wrapper class. You can add the .resx file from Project -> Add New Item... -> Resource File. Then you can reference this library from all your F# projects and use the generated wrapper class:
open ClassLibrary1; // this is the C# library
...
let x = Resources.String1;
The second approach is to add a .resx file directly to your F# project. The funny thing here is that Visual Studio won't allow you to "Add new item" of type Resource. You can workaround this by creating Resource file in another, temporary project - for example of type C# class library, and then add the generated .resx file to your F# project using the Project -> Add Existing Item command. Then you can use the resources from the resx file using the ResourceManager class (of course without the generated properties for every resources, as in the previous case):
open Library1
open System.Resources
[<EntryPoint>]
let main argv =
// We are in the console application, which references Library1
// Library1 contains the .resx file.
let resourceManager = ResourceManager("Resources", typeof<Library1.Class1>.Assembly);
let x = resourceManager.GetString("String1");
0
Note that in the upper case I have added the resource file directly in the project (not in subfloder), as a file named Resources.resx.
You can additionally create your custom wrapper which encapsulates such calls within properties.
Turns out I managed to do something that works like I intended, but instead of using the deserialized objects, I used the xml file itself.
Here's what I did:
First I changed the XML file's Build Action from Content to Resource by going Right-click the file -> Properties -> Build Action -> Resource
Then I went into the Xaml file, and added the following to Window.Resources
<XmlDataProvider
x:Key="DropDownData"
Source="/Resource;component/Strings.xml"
XPath="/Strings/String" />
In the drop-down menu that I needed the strings I added this:
ItemsSource="{Binding Source={StaticResource DropDownData}}"
And now my strings are beautifully displayed in the WPF gui.
Thanks for your suggestions though; they may come in useful in the future.
I have an application which I have written in MonoTouch. I dropped a file called ARCSDev into the applications folder and included it in the project, I then tried to read in the file using the following code:
private void generateChart (int chartNumber, string palette)
{
string path = Environment.GetFolderPath (Environment.SpecialFolder.Personal);
string filePath = Path.Combine(path, "ARCSDev");
Loader loader = new Loader (filePath);
However my application can't seem to find the file, can anyone explain what I'm doing wrong?
From a quick look you're trying to load from the "Documents" directory (i.e. the one you have read-write access, could access via iTunes and get backed up), while your own file is inside the application directory (which is read-only).
What you want is (likely) NSBundle.MainBundle.BundlePath
This Xamarin's article covers this (and a lot more) about the iOS file system.
I'm building a repository folder and file structure with many dependencies in version control using .NET for our data warehouse. Currently I have code to create dummy files and folders with C# code (see below). However, there are objects being shared. So I'd like to create shortcuts to Windows files and shortcuts to Windows folders as well as files. What would the code look like in C# to accomplish this?
Create Folder via C#.NET code:
string activeDir = #"C:\Source\EDW\dw-objects\trunk\table-objects";
string objectName = reader[0].ToString();
string newPath = System.IO.Path.Combine(activeDir, objectName);
System.IO.Directory.CreateDirectory(newPath);
Code varies depending on files based on format
Please use the ShellClass to create shortcuts also you will
need to get the special directory from desktop using Environment.SpecialFolder.DesktopDirectory
A very good example showing step by step can be found here http://www.codeproject.com/Articles/146757/Add-Remove-Startup-Folder-Shortcut-to-Your-App
Have a look at This Article it shows you how to manipulate NTFS Juncture points using c#. Because you will want to access the folders you need to have juncture points instead of shrotcuts since one is followed by machine processing and one isn't.
I have a Silverlight 4 / C# project I'm working on in Visual Studio. I made an XML data file by right clicking on the project >> Add New Item >> Xml File. I then try to open the file:
StreamReader streamReader = new StreamReader("data.xml");
However, this gives a security exception. How can I get around this, or grant the necessary permissions?
Silverlight doesn't allow local file system access by default. Your options are:
Using IsolatedStorage.
Run with elevated permissions.
Embedding the file in the assembly, if you only need to read it, as suggested by Jon Skeet.
If you need to store data in general, use IsolatedStorage if you can.
You would need to mark the item as a resource, NOT an embedded resource.
From MSDN...
The Properties window in Visual Studio
provides several other values in the
Build Action drop-down list. However,
you can use only the previous three
values with Silverlight projects. In
particular, Silverlight embedded
resources must always use the Resource
build action, and not the Embedded
Resource build action, which uses a
format that Silverlight cannot
recognize.
A great walk through can be seen here entailing what you are trying to accomplish. Since you are not trying to access files on disk but as resources this is not a problem. IsolatedStorage nor elevated permissions are pertinent here.
Do you just need to be able to read the file at execution time? If so, I would suggest you set it to have a Resource build action in Visual Studio, and then use Assembly.GetManifestResourceStream to open it. That's the simplest way of bundling read-only data with an application, IMO.
That constructor of StreamReader is expecting a file path into the local file system, which is only available out of browser with elevated trust.
Instead you should be using Application.GetResourceStream:-
Stream stream = Application.GetResourceStream(new Uri("data.xml", UriKind.Relative));
StreamReader reader = new StreamReader(stream);
However I expect you really just want this in an XDocument, you bypass this StreamReader stage:-
XDocument doc = XDocument.Load(stream);
BTW, I personally would leave the XML as Content in the Xap rather than embedding it in the assembly.