My first WinUI 3 project. My second C# project. I have been able to get the FileSavePicker to work and display a dialog:
What I am going to show is a stub program created just to workout the issue I am having with the FileSavePicker.
Issue description: I don't know how to use the non-async method 'CreateReport()' with the async FileSavePicker.
I have tested CreateReport() method with the 'Create Report' button, and it successfully creates an excel file and successfully writes to the file (using EPPlus).
How can I make the CreateReport() method wait for the FileSavePicker to finish and then use the file.name from the FileSavePicker?
My attempts to do it produce an excel file, but when I attempt to open the file, excel gives me this error:
XAML:
<Window
x:Class="App_WinUI3_FileSavePicker_Sandbox.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App_WinUI3_FileSavePicker_Sandbox"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center" Width="800" BorderBrush="black" BorderThickness="8">
<TextBlock Text="Test of File Save Picker and Creating an Excel file" HorizontalAlignment="Center" Margin="10" FontSize="20"/>
<StackPanel Orientation="Horizontal">
<Button x:Name="myButton" Click="myButton_Click" Margin="10" Padding="10">Start File Save Picker</Button>
<Button x:Name="Button2" Click="Button2_Click" Margin="10" Padding="10" ToolTipService.ToolTip="Tests the creation of an excel file using a hardcoded filename and not using the File Save Picker" >Create Report</Button>
</StackPanel>
<TextBox x:Name="ReportStatus1" Header="Report Status 1" Margin="10" FontSize="20"/>
<TextBox x:Name="ReportStatus2" Header="Report Status 2" Margin="10" FontSize="20"/>
<TextBox x:Name="ReportStatus3" Header="Report Status 3" Margin="10" FontSize="20"/>
<TextBox x:Name="ReportStatus4" Header="Report Status 4" Margin="10" FontSize="20"/>
<TextBox x:Name="ReportStatus5" Header="Report Status 5" Margin="10" FontSize="20"/>
</StackPanel>
</Window>
C# Code behind
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Provider;
using OfficeOpenXml;
// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
namespace App_WinUI3_FileSavePicker_Sandbox
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
// Retrieve the window handle (HWND) of the current WinUI 3 window.
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
// For EPPlus spreadsheet library for .NET
ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
}
public string File_Name = string.Empty;
private void myButton_Click(object sender, RoutedEventArgs e)
{
DisplayReportSaveDialog("Report1");
CreateReport(File_Name);
}
private async void DisplayReportSaveDialog(string ReportName)
{
FileSavePicker savePicker = new FileSavePicker();
// Retrieve the window handle (HWND) of the current WinUI 3 window.
var window = (Application.Current as App)?.m_window as MainWindow;
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
// Initialize the folder picker with the window handle (HWND).
WinRT.Interop.InitializeWithWindow.Initialize(savePicker, hWnd);
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Excel Workbook", new List<string>() { ".xlsx" });
// I don't want a text file. I need to create an excel file.
// savePicker.FileTypeChoices.Add("Plain Text", new List<string>() { ".txt" });
// Default file name if the user does not type one in or select a file to replace
switch (ReportName)
{
case "Report1":
savePicker.SuggestedFileName = "Report1_" String.Format("{0:MMddyyyy_HHmmss}", DateTime.Now) ".xlsx";
break;
case "Report2":
savePicker.SuggestedFileName = "Report2_" String.Format("{0:MMddyyyy_HHmmss}", DateTime.Now) ".xlsx";
break;
}
// Display the file picker dialog by calling PickSaveFileAsync
StorageFile file = await savePicker.PickSaveFileAsync();
if (file != null)
{
// Prevent updates to the remote version of the file until we finish making changes and call CompleteUpdatesAsync.
CachedFileManager.DeferUpdates(file);
// write the file name into the file.
// This is the sample code from the microsoft docs.
// It shows how to write simple text to a text file.
// It does work.
// Is there a similar way I can do this with my method 'CreateReport()'?
//await FileIO.WriteTextAsync(file, file.Name);
// Let Windows know that we're finished changing the file so the other app can update the remote version of the file.
// Completing updates may require Windows to ask for user input.
File_Name = file.Name;
// What does this do?
// Why did I add this?
//await file.DeleteAsync();
ReportStatus3.Text = $"path = {file.Path}";
ReportStatus4.Text = $"file_Name = {File_Name}";
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
if (status == FileUpdateStatus.Complete)
{
ReportStatus1.Text = "File " file.Name " was saved....";
}
else
{
ReportStatus1.Text = "File " file.Name " couldn't be saved.";
}
}
else
{
ReportStatus2.Text = $"{ReportName} report cancelled.";
}
}
private void CreateReport(string filename)
{
try
{
filename = "c:\\sandbox\\" filename;
ReportStatus5.Text = $"filename = {filename} - Message from (CreateReport())";
using (var package = new ExcelPackage(filename))
{
// EPPLUS - Add a new worksheet.
var ws = package.Workbook.Worksheets.Add($"EMB High Level Goals");
ws.Cells["A1"].Value = "1";
ws.Cells["A2"].Value = "2";
ws.Cells["B1"].Value = "Lenny";
ws.Cells["B2"].Value = "Created by App_WinUI3_FileSavePicker_Sandbox";
package.Save();
} // end using
ReportStatus3.Text = $"CreateReport: File {filename} was saved....";
}
catch (Exception e)
{
ReportStatus4.Text = $"CreateReport error: {e.Message}";
}
}
private void Button2_Click(object sender, RoutedEventArgs e)
{
string filename = "Test_Report_" String.Format("{0:MMddyyyy_HHmmss}", DateTime.Now) ".xlsx";
CreateReport(filename);
}
}
}
CodePudding user response:
Just remove commented Microsoft stuff:
//await FileIO.WriteTextAsync(file, file.Name);
//await file.DeleteAsync();
and instead of that call your own function:
CreateReport(file.Name);
CodePudding user response:
You should await the DisplayReportSaveDialog
before you call CreateReport
.
For you to be able to do this, you need to change the return type of the former method from void
to Task
so you code will look like this:
private async void myButton_Click(object sender, RoutedEventArgs e)
{
await DisplayReportSaveDialog("Report1");
CreateReport(File_Name);
}
private async Task DisplayReportSaveDialog(string ReportName)
{
//same code as before...
}
private void CreateReport(string filename)
{
//same code as before...
}