How to save a csv or txt in Xamarin UWP app? - xamarin

Today I've got a question about saving a .csv or .txt file within a Xamarin app on UWP platform. I am trying to save a file I create in my code call tags.csv. My goal is to have no .csv's saved initially, I create an instance in my code, then save it and create a new .csv file when my code executes. The creation and filling of the .csv occurs in one function which triggers based on a Button instance in my app. Also, ideally I could make it save in a location determined by a file explorer popup.
I have tried two routes so far to make and save a .csv file, the CSVExport package and CSVhelper package. Both I have been able to download and add to my project from NuGet successfully.
I have tried separately a simple implementation of each, basically just taking their Example code to see if it would work in my UWP app. Here is the respective code
// CSVExport methods, two ways to save
var myExport = new CsvExport();
...
File(myExport.ExportToBytes(), "text/csv", "results.csv"); // method 1
myExport.ExportToFile("./results.csv"); // method 2
// CSVhelper method
var records = new List<Foo>
{
new Foo { Id = 1, Name = "one" },
};
using (var writer = new StreamWriter("./tags.csv"))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(records);
}
Here is the error I am receiving: System.UnauthorizedAccessException: 'Access to the path C:...(filepath)...BLE.Client.UWP\bin\x86\Debug\AppX\tags.csv' is denied.'
Whenever the code reaches my saving of the .csv file, it crashes the app and Visual Studio 2022 gives me this error message. The same exact error occurs whether I am using CSVExport or CSVhelper.
Attempted Solutions:
My attempted solutions are mainly in regards to giving the app the permissions it needs to save. If an alternative like getting a different CSV package is better, I would take that advice too.
One solution I saw on StackOverflow linked to this page. The issue is I cannot load StorageFolder or Windows.Storage in my Xamarin app, it just won't recognize it and won't compile cause it's a missing load action.
Another solution I saw was changing your Capabilities in the Package.appxmanifest file and changing your Package header. I have done so, so mine looks like the following code sample. I need the internetClient and bluetooth and location for the app itself, so I added broadFilesystemaccess and even documents and pictures just to see if that would work too.
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap mp rescap">
...
<Capabilities>
<Capability Name="internetClient" />
<rescap:Capability Name="broadFileSystemAccess" />
<uap:Capability Name="documentsLibrary"/>
<uap:Capability Name="picturesLibrary" />
<DeviceCapability Name="bluetooth" />
<DeviceCapability Name="location"/>
</Capabilities>
Another solution was making sure the UWP app had permissions, which I went into system settings and allowed, so it should have full access now.
I am not sure where to go from here, so any advice about UWP or saving files within Xamarin UWP apps would be appreciated.

Based on your requirement, you could try to use the DependencyService feature of Xamarin.Forms.
DependencyService enables you to invoke native platform functionality from shared code. For your scenario, you could pass the stream of the file to the DependencyService first. then you could call the UWP FileSavePicker using DependencyService in your Forms app and save the stream as a file.
Here are some code snippets about how to implement the interface.
SaveFileAsync
public async Task SaveFileAsync(Stream data)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Text", new List<string>() { ".txt" });
savePicker.FileTypeChoices.Add("CSV", new List<string>() { ".csv" });
// Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = "New Document";
StorageFile file = await savePicker.PickSaveFileAsync();
if (file!= null)
{
using (IRandomAccessStream dataStream= data.AsRandomAccessStream())
{
using (var reader = new DataReader(dataStream.GetInputStreamAt(0)))
{
await reader.LoadAsync((uint)dataStream.Size);
var buffer = new byte[(int)dataStream.Size];
reader.ReadBytes(buffer);
await Windows.Storage.FileIO.WriteBytesAsync(file, buffer);
}
}
}
}
ISaveFileServicecs interface
public interface ISaveFileServicecs
{
Task<Stream> SaveFileAsync(Stream stream);
}
Usage
await DependencyService.Get<ISaveFileServicecs>().SaveFileAsync(stream);

Related

How to access file stored in shared project in Xamarin Forms?

I have a folder called Documentation inside the shared project, named App2 in this case. How do I access the files stored inside the Documentation folder? Attached image below shows the project structure.
Visual Studio Solution Page
I have tried out following commands but they aren't working :
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
AppDomain.CurrentDomain.BaseDirectory
System.Reflection.Assembly.GetExecutingAssembly().CodeBase;
If it's troublesome to access the file in that folder, I'm open to hearing other alternatives.
This is how I have done it for JSON files in my shared project (using PCL). As Jason pointed out in the comments, if you are using .NET Standard, you can simply define the GetSharedFile method in your shared project instead of creating platform specific references.
Add the file to the shared project and set it as Embedded Resource
Create an IFileHelper interface in your shared project
public interface IFileHelper {
Stream GetSharedFile(string fileResourceName);
}
Create a new FileHelper class in each project (Android and iOS) with the following
public class FileHelper : IFileHelper {
public Stream GetSharedFile(string fileResourceName) {
Type type = typeof(IFileHelper); // We could use any type here, just needs to be something in your shared project so we get the correct Assembly below
return type.Assembly.GetManifestResourceStream(fileResourceName);
}
}
Add a documentation handler class in your shared project. Something like below (make sure to change the App namespace to match yours):
public class Documentation {
private const string ResourcePath = "App.Documentation.index.html"; // App would be your application's namespace, you may need to play with the Documentation path part to get it working
public string GetDocs() {
IFileHelper helper = DependencyService.Get<IFileHelper>(); // Your platform specific helper will be filled in here
Stream stream = helper.GetSharedFile(ResourcePath);
using (stream)
using (StreamReader reader = new StreamReader(stream)) {
return reader.ReadToEnd(); // This should be the file contents, you could serialize/process it further
}
}
}
I wrote this mostly by hand so let me know if something is not working. If you cannot load your file, I suggest trying to put it into the root of your shared project and then changing ResourcePath in the code above to the following (again using your app's namespace instead of App):
private const string ResourcePath = "App.index.html";

Google service object for Google Calendar API

I am trying to use the Google Calendar API in .NET, specifically I am trying to get a list of events. According to the examples here, in different programming languages I need to create a 'service' object and an 'event' object. However, I can't find a clear explanation of what either of these objects is or how to initiate them. Does anyone have an explanation? Or can anyone provide any information or give me a link to where this is explained? It doesn't necessarily have to be in .NET
Here is the example in Java:
String pageToken = null;
do {
events = service.events().list('primary').setPageToken(pageToken).execute();
List<Event> items = events.getItems();
for (Event event : items) {
System.out.println(event.getSummary());
}
pageToken = events.getNextPageToken();
} while (pageToken != null);
Following the advice answered, I am getting the following error:
Could not load file or assembly 'Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.16.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
Here is the code, the error occurs on the credentials = Await... line
Dim credential As UserCredential
Dim clientSecretsPath As String = Server.MapPath("~/App_Data/client_secret.json")
Dim scopes As IList(Of String) = New List(Of String)()
scopes.Add(CalendarService.Scope.Calendar)
Using stream = New System.IO.FileStream(clientSecretsPath, System.IO.FileMode.Open, System.IO.FileAccess.Read)
credential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, scopes, "user", CancellationToken.None)
End Using
The problem with GoogleWebAuthorizationBroker is that it tries to launch a new instance of a web browser to go and get authorization where you have to click the "Grant" button.
Obviously if you're running a MVC project under IIS it's just going to get confused when the code tries to execute a web browser!
My solution:
Download the .net sample projects: https://code.google.com/p/google-api-dotnet-client/source/checkout?repo=samples
Build and run one of the projects relevant to you (Eg Calendar or Drive). Dont forget to include your client_secret.json file downloaded from the cloud console.
Run the project and it will open a new browser on your computer where you will have to click the "Grant" button. Do this once and then your MVC code will work because it will not try to open a web browser to grant the permissions.
I'm not aware of any other way to grant this permission to the SDK but it worked for me just great!
Good luck. This took me a good 5 hours to figure out.
Just had the same issue running VS2013 (using .net45 for my project):
After fetching the CalendarV3 API via NuGet you just have to manually add the reference to:
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
to the project (because it is not inserted automatically via the NuGet-Script)!
That's it! Maybe #peleyal is correcting the script somewhen in future ;)
Remember that this sample is for Java. My recommendation is to do the following:
Take a look in our VB sample for the Calendar API which is available here
You should take a look also in other sample for C#, let's say Tasks API sample
Start a new project and add a NuGet reference to Google.Apis.Calednar.v3. Remember that it's prerelease version.
Your code should look like the following:
It's based on the 2 samples above, I didn't compile or test it but it should work.
UserCredential credential;
using (var stream = new System.IO.FileStream("client_secrets.json",
System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] { CalendarService.Scope.Calendar },
"user", CancellationToken.None);
}
// Create the service.
var service = new CalendarService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "YOUR APP NAME HERE",
});
var firstCalendar = (await service.CalendarList.List().ExecuteAsync()).Items().FirstOrDefault();
if (firstCalendar != null)
{
// Get all events from the first calendar.
var calEvents = await service.Events.List(firstCalendar.Id).ExecuteAsync();
// DO SOMETHING
var nextPage = calEvents.NextPage;
while (nextPage != null)
{
var listRequest = service.Events.List(firstCalendar.Id);
// Set the page token for getting the next events.
listRequest.PageToken = nextPage;
calEvents = await listRequest.EsecuteAsync();
// DO SOMETHING
nextPage = calEvents.NextPage;
}
}
I had the same error, and it was due to the app trying to launch the accept screen.
I first tried to get the vb.net example from google and ran that, which I did get to work, and change to my secret info, ran and got the accept screen. I then tried my app, and it still did not work.
I noticed that the dll was found here under my project installed from the nuget packages.
...packages\Microsoft.Bcl.Async.1.0.165\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll
but was not in the net45 dir. So I uninstalled the nuget packages (have to if changing the .net version) then changed my .net version for my project to 4.0 instead of 4.5, reinstalled the nuget packages, and then it worked!!

Opening a PDF file in Windows Phone

I'm developing an app for Windows Phone 7 and I'm using a Phonegap template for it.
Everything looks perfect, but now I’m stuck trying to open a PDF file in the browser.
I tried the following but that doesn’t work because the url of the PDF exceeds the 2048 character limit (it’s a data url). This code runs after the deviceReady event was fired.
var ref = window.open('http://www.google.com', '_blank', 'location=no');
ref.addEventListener('loadstart', function () { alert(event.url); });
Now, I'm trying to save the PDF file to storage and then I'm trying to have it opened by the browser, but the browser doesn't show anything. I'm editing the InAppBrowser.cs code from cordovalib and I added the following lines before calling browser.Navigate(loc);
private void ShowInAppBrowser(string url)
{
IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
FileStream stream = store.OpenFile("test.pdf", FileMode.Create);
BinaryWriter writer = new BinaryWriter(stream);
var myvar = Base64Decode("the big data url");
writer.Write(myvar);
writer.Close();
if (store.FileExists("test.pdf")) // Check if file exists
{
Uri loc = new Uri("test.pdf", UriKind.Relative);
...
}
}
This code is returning the following error:
Log:"Error in error callback: InAppBrowser1921408518 = TypeError: Unable to get value of the property 'url': object is null or undefined"
I don’t wanna use ComponentOne.
Any help would be greatly appreciated!
You cannot open pdf files from the isolated storage in the default reader for PDF files. If the file is online e.g. it has a URI for it, you can use WebBrowserTask to open it since that will download and open the file in Adobe Reader.
On Windows Phone 8 you actually can open your own file in default file reader for that extension, but I am not sure how that will help you since you target PhoneGap and Windows Phone 7.
Toni is correct. You could go and try to build your own viewer (which would be the same thing as using C1, but with more time involved). I worked on a port of iTextSharp and PDFSharp for WP7, but neither of which are PDF Viewers. They are good for creating PDFs and parsing them some (but to render them there is more work involved). This has been a personal quest of mine, but honestly the best I have gotten is to be able to extract some images from the PDF (and none of the text)
try this
var installedLocation = Windows.ApplicationModel.Package.Current.InstalledLocation;
var assets = await installedLocation.GetFolderAsync("Assets");
var pdf = await assets.GetFileAsync("metro.pdf");
Windows.System.Launcher.LaunchFileAsync(pdf);
This worked correctly on my Device.

Is it possible to upload image or file to SkyDrive fom Metro Style App?

Is it possible to upload image or file to SkyDrive fom Metro Style App?
I have already found how to browse the file from SkyDrive. But I haven't found regarding uploading file to SkyDrive. If you reply me, it will be very thankful..
I don't think the file picker method works unless the user has the desktop app installed.
You should use a Sharing contract. If you add a data file (Storage Item) to share, then SkyDrive will be listed as a share target and the user gets a UI where they can choose where in their SkyDrive they want to save. This is how I implemented it in my app.
For more info...
http://msdn.microsoft.com/en-us/library/windows/apps/hh771179.aspx
You can use FileSavePicker to save files. This will of course give the user a chance to select where he wants to save to local documents folder or sky drive. The user is in control.
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
savePicker.DefaultFileExtension = ".YourExtension";
savePicker.SuggestedFileName = "SampleFileName";
savePicker.FileTypeChoices[".YourExtension"] = new List<string>() { ".YourExtension"};
StorageFile file = await savePicker.PickSaveFileAsync();
if (file != null)
{
await FileIO.WriteTextAsync(file, "A bunch of text to save to the file");
}
Please note that in the sample code I am creating the content of the file in code. If you want the user to select an existing file from the computer then you will have to first use FileOpenPicker, get the file and then use FileSavePicker to save the contents of the selected file to the SkyDrive
Assuming that you are using XAML/JavaScript, the suggested solution is to use FilePicker.
The following link may help you.
http://msdn.microsoft.com/en-us/library/windows/apps/jj150595.aspx
Thanks Mamta Dalal and Dangling Neuron, but there is problem. But it looks like I can't use FileSavePicker. I have to upload file(documnet, photo) not only text file. I have to copy from one path to another. If I use FileSavePicker, I have to write every file content (text, png, pdf, etc) and can't copy. Currently I am using FolderPicker. But unfortunately, FolderPicker doesn't support SkyDrive.My Code is As follow:
>FolderPicker saveFolder = new FolderPicker();
>saveFolder.ViewMode = PickerViewMode.Thumbnail;
>saveFolder.SuggestedStartLocation = PickerLocationId.Desktop;
>saveFolder.FileTypeFilter.Add("*");
>StorageFolder storagefolderSave = await saveFolder.PickSingleFolderAsync();
>StorageFile storagefileSave = [Selected storagefile with file picker];
>await storagefileSave.CopyAsync(storagefolderSave,storagefileSave.Name,NameCollisionOption.ReplaceExisting);
It will be greate that if FolderPicker supports SkyDrive or can copy file using FileSavePicker.

How to read Asset Files using VS & Monodroid

I've been trying to implement this example using C# and Monodroid, but am having difficulties reading and writing an Asset file:
http://docs.xamarin.com/android/advanced_topics/using_android_assets
I am using the emulator, not a device.
First of all, I am having trouble finding the namespace for Assets.Open. What I ultimately found was
const string lfn = MyAssetFile.txt;
System.IO.StreamReader(Android.Content.Res.Resources.System.Assets.Open(lfn);
Is this the correct namespace?
Second of all, my Asset file is marked as AndroidAsset and "Copy Always" in the VS "Properties" pane, but my attempts to read the file always fail (File Not Found) using this statement:
string settings = "";
using (StreamReader sr = new System.IO.StreamReader (Android.Content.Res.Resources.System.Assets.Open(lfn))) settings = sr.ReadToEnd();
Do I have my VS settings wrong so that the asset file not being copied onto the emulator, or is it being copied OK but my code to open/read it is wrong?
You must use:
const string lfn = "MyAssetFile.txt";
string settings = string.Empty;
// context could be ApplicationContext, Activity or
// any other object of type Context
using (var input = context.Assets.Open(lfn))
using (StreamReader sr = new System.IO.StreamReader(input))
{
settings = sr.ReadToEnd();
}
If I remember correctly, as I haven't got an IDE at the moment, Android.Content.Res.Resources.System.Assets references the Android assets not your project assets. You want to use your projects assets so you need to use the AssetManager from your Activities or Contexts.
For example: the Activity class has a property called Assets. This is what you use. OR you use a View's Context.Assets property.
Or just simply add at the top:
using System.IO;
and it will work.
Xamarin team forget sometimes to mention using stuff.

Resources