How to load, edit and save xml in uwp app - c#

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.

Related

No permission to delete / overwrite files in subfolders of ApplicationData.Current.LocalFolder

I am facing an problem with files that I create within my application in dedicated sub folders of ApplicationData.Current.LocalFolder. I can create sub folders with ApplicationData.Current.LocalFolder.CreateFolderAsync() and even place files in it. But as soon as I try to overwrite or delete files, I get an access violation exception. (Read Access is still possible)
Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))
I looked up the UWP pages but most of the time they simply create a new folder and that's it.
My attempt to overwrite the file:
public async Task WriteFileAsync(string filename, Stream fileContent)
{
StorageFolder folder = ApplicationData.Current.LocalFolder;
var deepFolders = filename.Split('/');
if (deepFolders != null)
{
for (var i=0; i<deepFolders.Length - 1; i++)
{
folder = await folder.GetFolderAsync(deepFolders[i]);
}
filename = deepFolders[deepFolders.Length - 1];
}
try
{
StorageFile oldFile = await folder.GetFileAsync(filename);
await oldFile.DeleteAsync();
}
catch (FileNotFoundException) { }
StorageFile file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
}
The first time it runs just fine, but as soon as the overwrite should take place, I get the exception.
Running the whole stuff without sub-folders works like charm.
The attempt to first read and delete the file resulted in the same exception already during the deletion.

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

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()));

Windows phone 8 and Surface pro 2 OpenStreamForWriteAsync blocks the app

I'm working on a project for windows phone and surface pro 2 app and I'm looking for reading a text from a .txt file.
I'm new in windows app development and I take over the project of an other guy.
I didn't change his code for the moment but there are some things I don't understand and the main problem is that when I use the app, this one is blocked when I call the method OpenStreamForWriteAsync().
Here is the code:
public async void FillIn()
{
Stream stream1 = null;
StorageFolder storageFolder = KnownFolders.DocumentsLibrary; // Gets Documents library
//The following structure code in comment is from me, is it necessary to
//do all these verification or "StorageFolder storageFolder =
//KnownFolders.DocumentsLibrary;" is enough ?
//try
//{
// folder = await KnownFolders.DocumentsLibrary.GetFolderAsync(AppFolderName);
//}
//catch (Exception)
//{
// // Folder does not exist, but we cannot await in a catch block, so we can not create it here.
//}
//if (folder == null)
//{
// folder = await KnownFolders.DocumentsLibrary.CreateFolderAsync(AppFolderName, CreationCollisionOption.OpenIfExists);
//}
// Test if the file exist
StorageFile report1 = null;
try
{
report1 = await storageFolder.GetFileAsync("report_final.txt"); // Take the .txt file.
}
catch { }
if (report1 == null)
{
// Could not find file
}
// PROBLEM IS HERE
stream1 = await report1.OpenStreamForWriteAsync(); // the app is blocked at this line
using (StreamReader sr1 = new StreamReader(stream1))
{
Texte = sr1.ReadToEnd();
sr1.Dispose(); // the guy before me use this but when we use "using"
//isn't it suppose to call "Dispose" at the end automatically ??
}
}
Thanks for your help and feel free to ask me more information if you don't understand something.

phone:webbrowser does not open PDF files (Windows Phone 8)

I have build an app with a webbrowser in it. It's working fine but when I try to navigate to an adress like bla.pdf the webbrowser shows nothing.
I solved this problem with automatically open the Internet Explorer if the adress is linking to a pdf file.
Is there a better solution? I want to open that PDF file in my own app and I dont want to open the Internet Explorer everytime. Any suggestions?
If you've got a locally downloaded PDF that is in Isolated Storage you can launch the PDF Reader application (or any other applications registered to open PDF files) using LaunchFileAsync.
private async void LaunchFileButton_Click(object sender, RoutedEventArgs rea)
{
// Access isolated storage.
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
// Access the PDF.
StorageFile pdfFile = await local.GetFileAsync("file1.pdf");
// Launch the bug query file.
Windows.System.Launcher.LaunchFileAsync(pdfFile);
}
(adapted from MSDN, see section on "launching a file").
If it's a remote URL then you can use LaunchUriAsync (which will use IE to download the file first).
You will need to try this on a device with the PDF Reader application installed - it won't work on the Emulator.
You should read following article if you are not familiar with Async: MSDN Asynchronous Programming with Async and Await
I couldn't test my app because my WP8 Phone is currently not available and I can't install an PDF reader on the emulator.
Call following method to start the download
WebClient pdfDownloader = null;
string LastFileName = ""; //To save the filename of the last created pdf
private void StartPDFDownload(string URL)
{
pdfDownloader = new WebClient(); //prevents that the OpenReadCompleted-Event is called multiple times
pdfDownloader.OpenReadCompleted += DownloadPDF; //Create an event handler
pdfDownloader.OpenReadAsync(new Uri(URL)); //Start to read the website
}
async void DownloadPDF(object sender, OpenReadCompletedEventArgs e)
{
byte[] buffer = new byte[e.Result.Length]; //Gets the byte length of the pdf file
await e.Result.ReadAsync(buffer, 0, buffer.Length); //Waits until the rad is completed (Async doesn't block the GUI Thread)
using (IsolatedStorageFile ISFile = IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
LastFileName = "tempPDF" + DateTime.Now.Ticks + ".pdf";
using (IsolatedStorageFileStream ISFileStream = ISFile.CreateFile(LastFileName))
{
await ISFileStream.WriteAsync(buffer, 0, buffer.Length);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + Environment.NewLine + ex.HResult,
ex.Source, MessageBoxButton.OK);
//Catch errors regarding the creation of file
}
}
OpenPDFFile();
}
private async void OpenPDFFile()
{
StorageFolder ISFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
try
{
IStorageFile ISFile = await ISFolder.GetFileAsync(LastFileName);
await Windows.System.Launcher.LaunchFileAsync(ISFile);
//http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206987%28v=vs.105%29.aspx
}
catch (Exception ex)
{
//Catch unknown errors while getting the file
//or opening the app to display it
}
}
To call these methods from your WebBrowser-Control you need to catch the navigating event.
YourWebBrowserControl.Navigating += YourWebBrowserControl_Navigating;
void YourWebBrowserControl_Navigating(object sender, NavigatingEventArgs e)
{
if(e.Uri.AbsolutPath.EndsWith("pdf"))
{
StartPDFDownload(e.Uri.ToString());
}
}
Don't forget that you'll have to delete the files created someday.
Try this to open a PDF from a WebControl:
void MyWebBrowserControl_Navigating(object sender, NavigatingEventArgs e)
{
if (e.Uri.AbsolutPath.ToLower().EndsWith(".pdf"))
{
var success = Windows.System.Launcher.LaunchUriAsync(e.Uri);
}
}

Categories

Resources