Accessing text files in Windows 8 apps - c#

I would like to set up a set of local text 'data' files and display them on a windows 8 app.
I can use something like this in a class:
public async void readFile()
{
StorageFile storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/Box__The.txt"));
Stream stream = await storageFile.OpenStreamForReadAsync();
StreamReader streamReader = new StreamReader(stream);
_strFile = streamReader.ReadToEnd();
}
which reads the file into a text string correctly and I can see that when I step through the code, but when I try to pass the result back to the main display code, it shows it as null.
Is this some issue to do with threads or something?

try
{
await readFile();
label.Text = _strFile ;
}
catch (Exception ex)
{
// An exception occurred from the async operation
}

Related

How to load, edit and save xml in uwp app

Title says it all really. I've been stuck on this one for days and would appreciate some help. I've a main page and a settings page when the main page loads first time it tests for settings.xml in local folder and copies it if not found. Then when the user opens settings page it's supposed to load details from local folder allowing the user to edit before saving them back to the local folder from OnNavigatedFrom event.
Code to load from installation folder to local folder
// Has the file been copied already?
bool blFileExist = false;
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
// No exception means it exists
blFileExist = true;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.White);
}
catch (System.IO.FileNotFoundException)
{
// The file obviously doesn't exist
blFileExist = false;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.Red);
}
catch (Exception)
{
}
if (!blFileExist)
{
try
{
// Cant await inside catch, but this works anyway
StorageFile stopfile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("settings.xml");
await stopfile.CopyAsync(ApplicationData.Current.LocalFolder);
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception)
{
}
}
Code to load and save settings page
private void loadSettings()
{
try
{
doc = XElement.Load("settings.xml");
nAIPlayers = int.Parse(doc.Element("ai_players").Value);
strCardBack = doc.Element("back").Value;
comboBoxAIPlayers.SelectedIndex = nAIPlayers - 1;
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}
}
private async void saveSettings()
{
//try
//{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using (Stream fileStream = await file.OpenStreamForWriteAsync())
{
doc.SetElementValue("ai_players", nAIPlayers);
doc.SetElementValue("back", "Back_0");
doc.Save(fileStream);
}
/*}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}*/
}
I think the problem is I'm accessing the local file to save it and the installation file to load it. The result is no matter what I save it always reads the values in the original settings.xml
How do I load this from the local folder?
doc = XElement.Load("settings.xml");
Update
On the first iteration the code runs fine and the settings page code opens as it should. It's only after leaving the settings page and running saveSettings() method that it fails and throws an error when reloading the settings page and running loadSettings().
System.Xml.XmlException: Data at the root level is invalid. Line 5, position 12
You are doing it wrong because you are using XElement.Load(string) where string stands for URI, which in this case should be:
The Uri parameter must be a file system relative or absolute path.
and with that you will have a problem in UWP as normally you don't have the permission. It also won't work here with URIs like: "ms-appdata:///settings.xml".
Probably you can read a path to your LocalFolder and use it (may work, though haven't tested it), but much easier is to load the content from stream (or read string from file and then load XML from that string), for example like this:
var file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using(var stream = await file.OpenStreamForReadAsync())
{
var doc = XElement.Load(stream);
// ...
}
Note also that there are other classes like XDocument or XmlDocument where you can load and manage you xml file. Everything depends on your needs.

Json serializer - how to create a stream

public static async Task Store(ObservableCollection<Product> list)
{
Uri path = new Uri("ms-appx:///ListCollection.json");
var store = await StorageFile.GetFileFromApplicationUriAsync(path);
var stream = File.OpenWrite(store.Path);
var serialize = new DataContractJsonSerializer(typeof(ObservableCollection<Product>));
serialize.WriteObject(stream, list);
}
Ok this is the piece of code that I used to serialize a collection , works very well , no problem with it , but what I want and tried and no success. I created a JSON file in my project. I want to store and stream data to that file. I tried some methods but no success , how do I open a stream to a file that is currently in my project?
EDITED : Commented the code that was working and wrote what I intend to do. Thanks for support.
When I get to this line
var stream = File.OpenWrite(store.Path); it says that is inaccesible.
What I intend to do is serialize some data to a file called ListCollection.json that is emtpy , that file is project file. It might be the stream or it might be the file that gives me that error. No idea.
My guess is that your project file is located in the installation directory of your application and as far as I know you can't just write to that directory.
You would have to put a deployment action in your solution that writes the desired project file to the application data directory. There you should be able to write it.
I looked through some of the documentation and came accross this:
MSDN
The app's install directory is a read-only location.
I found a Link which makes use of a little hack or so it seems.
I am not sure if this will work if the application is deployed etc.
but you can try this to write the file.
I am not sure if you need a stream or not but feel free to comment:
private void Button_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<string> list = new ObservableCollection<string>();
list.Add("Hallo");
list.Add("Welt");
Task t = Store(list);
}
public static async Task Store(ObservableCollection<string> list)
{
StorageFile file = await GetStorageFileFromApplicationUriAsync();
if (file == null)
{
file = await GetStorageFileFromFileAsync();
}
if (file != null)
{
await file.DeleteAsync();
await CreateFileInInstallationLocation(list);
}
}
private static async Task<StorageFile> GetStorageFileFromFileAsync()
{
StorageFile file = null;
if (file == null)
{
try
{
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
file = await folder.GetFileAsync("ListCollection.json");
}
catch
{ }
}
return file;
}
private static async Task<StorageFile> GetStorageFileFromApplicationUriAsync()
{
StorageFile file = null;
try
{
Uri path = new Uri("ms-appx:///ListCollection.json");
file = await StorageFile.GetFileFromApplicationUriAsync(path);
}
catch
{ }
return file;
}
private static async Task CreateFileInInstallationLocation(ObservableCollection<string> list)
{
var pkg = Windows.ApplicationModel.Package.Current;
var installedLocationFolder = pkg.InstalledLocation;
try
{
var file = await installedLocationFolder.CreateFileAsync("ListCollection.json", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
var filePath = file.Path;
DataContractJsonSerializer serialize = new DataContractJsonSerializer(typeof(ObservableCollection<String>));
using (Stream stream = await file.OpenStreamForWriteAsync())
{
serialize.WriteObject(stream, list);
stream.Flush();
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
What this basically does is:
Find the file
Delete the file
Create a new file
Write your JSON to the file
I am really not an expert on this matter and it even to me seems pretty hacky but it apparently does the job.
If you can avoid writing to the install directory do it and use the method Frank J proposed

Cannot access LocalFolder files on Windows phone app (UnauthorizedAccessException)

I am trying to get an application to write (then read) a simple text file in Windows Phone 8. My app has three controls: a create file button, a display file button, and a textbox where the contents are supposed to display.
I have the following events set up in my code:
private async void btnCreateFile_Click(object sender, RoutedEventArgs e)
{
await ApplicationData.Current.LocalFolder.CreateFileAsync("myFile.txt");
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("myFile.txt");
StreamWriter writer = new StreamWriter(await file.OpenStreamForWriteAsync());
writer.WriteLine("Hello World");
writer.WriteLine("Goodbye world");
}
private async void btnShowFile_Click(object sender, RoutedEventArgs e)
{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("myFile.txt");
StreamReader reader = new StreamReader(await file.OpenStreamForReadAsync());
string text = reader.ReadToEnd();
text1.Text = text;
}
}
The application is throwing UnauthorizedAccessException when the StreamReader is being created. Can anyone shed any light on why?
I guess you're not disposing the StreamWriter. See the example on MSDN.
using( var writer = new StreamWriter(await file.OpenStreamForWriteAsync()))
{
writer.WriteLine("Hello World");
writer.WriteLine("Goodbye world");
}
That's why your can't read the file, because it's already taken by SreamWriter.
Always use 'using' statement when using an object that inherits from IDisposable.
You are trying to write to an already opened file and this gives you UnauthoritedException.
Try using your code block inside of using.Check out this question to find more about StreamWriter :
how to use StreamWriter class properly?

StorageFolder File Not Found error

I am having some issues in my app that can download a list of music files. I'm trying to setup the following folder structure. Music Library > Artist(s) > Release Name. When starting the download, the first song's folder structure is setup properly. Once the second download starts, I always get a File Not found exception when trying to create the second sub folder (release name). Here is my code.
private async Task StartDownload(List<DownloadData> data)
{
foreach (DownloadData song in data)
{
// Set the source of the download
Uri source = new Uri(song.downloadUrl);
// Create folder stucture
StorageFolder artistFolder;
try
{
artistFolder = await KnownFolders.MusicLibrary.CreateFolderAsync(song.artistName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw;
}
StorageFolder releaseFolder;
try
{
releaseFolder = await artistFolder.CreateFolderAsync(song.releaseName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw; // Exception Thrown here
}
// Create file
StorageFile destinationFile;
try
{
destinationFile = await releaseFolder.CreateFileAsync(song.fileName, CreationCollisionOption.GenerateUniqueName);
}
catch
{
throw;
}
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destinationFile);
List<DownloadOperation> requestOperations = new List<DownloadOperation>();
requestOperations.Add(download);
await HandleDownloadAsync(download, true);
}
}
I have no idea why it works the first time around but fails on the second song.
According to the documentation for CreateFileAsync it will throw FileNotFoundExcption if
The folder name contains invalid characters, or the format of the folder name is incorrect.
So you likely need to replace invalid characters with something else like underscore.
var fixedFolderName = string.Join(
"_",
song.releaseName.Split(Path.GetInvaildFileNameChars()));

How to read from StorageFile until a character in found in Windows Store app?

I have an app for Windows Store and what i am trying to do is read text from a file. I have two textFields. The descriptionTextField accepts new lines.
// Read from file
public async Task ReadFile()
{
try
{
// get the file
StorageFile notesStorageFile = await localFolder.GetFileAsync("NotesData.txt");
var readThis = await FileIO.ReadLinesAsync(notesStorageFile);
foreach (var line in readThis)
{
notesRepository.Add(new Note(line.Split(';')[0], line.Split(';')[1]));
}
Debug.WriteLine("File read successfully.");
}
catch (FileNotFoundException ex)
{
Debug.WriteLine("Error1: " + ex);
}
}
Now if NotesData.txt has:
Eggs;description eggs;
it works file.
But if NotesData.txt has:
Groceries;buy 10 eggs
buy 1 kg meat;
I get the index out of bound error. I just cant figure out how to fix the ReadFile() code.
The exception appears when i am calling the method. The problem i believe is with the descriptionTextBox that can accept new lines.
NotesData.txt
Apples;description apples; // works ok
Pears; description line 1
description line 2
description line 3; // problem
Pears; description line 1; // works ok
It seems to me you're trying to read back contents of a file you have previously saved and the problems you're having are just a consequence of the format you have selected for saving the data in the first place. Looking at it, new lines are not the only difficulty you're going to be having. What if the user decides to enter a semicolon in one of the textboxes? Are you preventing that?
I suggest you abandon your own serialization format and rather use one of the existing ones. If your notesRespository is a List<Note> this could be your (de)serialization code for XML:
private async Task Save(List<Note> notesRepository)
{
var xmlSerializer = new XmlSerializer(typeof (List<Note>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync("notes.xml", CreationCollisionOption.ReplaceExisting))
{
xmlSerializer.Serialize(stream, notesRepository);
}
}
private async Task<List<Note>> Load()
{
var xmlSerializer = new XmlSerializer(typeof(List<Note>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("notes.xml"))
{
return (List<Note>) xmlSerializer.Deserialize(stream);
}
}
And this for JSON:
private async Task Save(List<Note> notesRepository)
{
var jsonSerializer = new DataContractJsonSerializer(typeof (List<Note>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForWriteAsync("notes.json", CreationCollisionOption.ReplaceExisting))
{
jsonSerializer.WriteObject(stream, notesRepository);
}
}
private async Task<List<Note>> Load()
{
var jsonSerializer = new DataContractJsonSerializer(typeof(List<Note>));
using (var stream = await ApplicationData.Current.LocalFolder.OpenStreamForReadAsync("notes.json"))
{
return (List<Note>)jsonSerializer.ReadObject(stream);
}
}
When the repository gets too large to always load and save it as a whole you could even consider a structured storage like SQLite.
This line:
notesRepository.Add(new Note(line.Split(';')[0], line.Split(';')[1]));
assumes that you'll always have at least one semi-colon in a line. If you've got a line in your file which doesn't have that (e.g. a blank line) then it will fail.
It's not clear where that's where your problem is, because you haven't said where the exception's coming from, but that would be my first guess.
I'd also only do the split once:
string[] bits = line.Split(';');
if (bits.Length >= 2)
{
// What do you want to do with lines with more than one semi-colon?
notesRepository.Add(bits[0], bits[1]);
}
else
{
// Handle lines without a semi-colon at all.
}

Categories

Resources