C# WinUI 3 我无法使用从 FileSavePicker 对话框指定的文件来创建 excel 报告文件
C# WinUI 3 I cannot use the file specified from a FileSavePicker dialog to create an excel report file
我的第一个 WinUI 3 项目。我的第二个 C# 项目。我已经能够让 FileSavePicker 工作并显示一个对话框:
我要展示的是一个存根程序,它只是为了解决我在使用 FileSavePicker 时遇到的问题而创建的。
问题描述:我不知道如何将非异步方法'CreateReport()'与异步FileSavePicker一起使用。
我已经用 'Create Report' 按钮测试了 CreateReport() 方法,它成功地创建了一个 excel 文件并成功写入该文件(使用 EPPlus)。
如何让 CreateReport() 方法等待 FileSavePicker 完成,然后使用 FileSavePicker 中的 file.name?
我尝试这样做会生成一个 excel 文件,但是当我尝试打开该文件时,excel 给我这个错误:
主窗口:
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# 代码隐藏
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);
}
}
}
只需删除评论的 Microsoft 内容:
//await FileIO.WriteTextAsync(file, file.Name);
//await file.DeleteAsync();
而不是调用您自己的函数:
CreateReport(file.Name);
您应该等待 DisplayReportSaveDialog
,然后再致电 CreateReport
。
为了能够做到这一点,您需要将前一个方法的 return 类型从 void
更改为 Task
,这样您的代码将如下所示:
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...
}
我的第一个 WinUI 3 项目。我的第二个 C# 项目。我已经能够让 FileSavePicker 工作并显示一个对话框:
我要展示的是一个存根程序,它只是为了解决我在使用 FileSavePicker 时遇到的问题而创建的。
问题描述:我不知道如何将非异步方法'CreateReport()'与异步FileSavePicker一起使用。
我已经用 'Create Report' 按钮测试了 CreateReport() 方法,它成功地创建了一个 excel 文件并成功写入该文件(使用 EPPlus)。
如何让 CreateReport() 方法等待 FileSavePicker 完成,然后使用 FileSavePicker 中的 file.name?
我尝试这样做会生成一个 excel 文件,但是当我尝试打开该文件时,excel 给我这个错误:
主窗口:
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# 代码隐藏
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);
}
}
}
只需删除评论的 Microsoft 内容:
//await FileIO.WriteTextAsync(file, file.Name);
//await file.DeleteAsync();
而不是调用您自己的函数:
CreateReport(file.Name);
您应该等待 DisplayReportSaveDialog
,然后再致电 CreateReport
。
为了能够做到这一点,您需要将前一个方法的 return 类型从 void
更改为 Task
,这样您的代码将如下所示:
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...
}