Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Public Spec for Microsoft.Windows.Storage.Pickers [Milestone 1] #5155

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions specs/StoragePickers/FileOpenPicker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
FileOpenPicker Class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec is split across multiple files, with their definitions (IDL) likewise split across multiple files. This complicates reviewing (and holistically understanding) the design and details.

Do you intend to have multiple .idl files in the implementation?

CONSIDER: Moving all the definitions to a single file, ideally as an # API section in Microsoft.Windows.Storage.Pickers.md

...
# API

```c# (but really MIDL3)
namespace Windows.Storage.Pickers
{
    ...
}

That will also address gaps e.g. where's the API contract details?

===

# Background

Namespace: [Microsoft.Windows.Storage.Pickers](./Microsoft.Windows.Storage.Pickers.md)

Represents a UI element that lets the user choose and open files.

Supports specifying the initial location, extension filters, and text on commit button.

# API Pages

## Constructor

### Attributes

| **Attribute** | **Type** | **Description** |
|--------------------------|---------------------------------------------------------|--------------------------------------------------------------------------|
| `ViewMode` | [Microsoft::Windows::Storage::Pickers::PickerViewMode](./PickerViewMode.md) | Gets or sets the view mode that the file picker is using to present items. |
| `SuggestedStartLocation` | [Microsoft::Windows::Storage::Pickers::PickerLocationId](./PickerLocationId.md)| Gets or sets the initial location where the file picker looks for files. |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a chance SuggestedStartLocationDirectory will be added, where you can provide any string file path (e.g. if SuggestedStartLocation == PickerLocationId.Unspecified)? (see #4942 (comment))

It would be a bummer if this limitation, that only applies to the UWP picker and UWP apps and not the COM picker, would be retained. From history, if it's not being addressed now, it will forever stay like that :/

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw: It's certainly a feature that would be much appreciated as @whiskhub said. There is a quite popular issue dotnet/maui#9212 on the MAUI side asking for this feature. A person did a great research into it here.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, a SuggestedStartLocationDirectory would be very useful. If you don't implement that, please at least implement the SettingsIdentifier, otherwise the start location will be even less customizable than in UWP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi WinAppSDK friends @whiskhub , @MartyIX , @benstevens48 .

The string input of SuggestedStartLocation feature is on our wishlist for future milestones. For the first version of Microsoft.Windows.Storage.Pickers, we aim to implement method signatures consistent with Windows.Storage.Pickers and will not add new signatures at this time.

Copy link
Member

@DrusTheAxe DrusTheAxe Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and will not add new signatures at this time

You are, to a degree, as the spec is a consistent subset of Windows.Storage.Pickers - no PickMultipleFilesAsync(), CreateForUser(), etc. Why the omissions if consistency is a goal?*

* I can see why the Continue properties/methods are omitted. It's the others I find puzzling if consistency is the goal.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am just afraid that "wishlist for future milestones" practically means "never" 😅 which means I'd "never" be able to use this picker :/

| `CommitButtonText` | `winrt::hstring` | Gets or sets the text displayed on the commit button of the file picker. |
| `FileTypeFilter` | `Windows::Foundation::Collections::IVector<hstring>` | Gets the collection of file types that the file picker displays. |

### Examples
C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var openPicker = new FileOpenPicker(this.AppWindow.Id)
{
// (Optional) specify the initial location. If not specified, use system default:
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,

// (Optional) specify the text displayed on commit button. If not specified, use system default:
CommitButtonText = "Choose selected files",

// (Optional) specify file extensions filters. If not specified, default to all (*.*)
FileTypeFilter = { ".txt", ".pdf", ".doc", ".docx" },
};
```

C++

```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileOpenPicker openPicker(AppWindow().Id());

// (Optional) specify the initial location. If not specified, use system default:
openPicker.SuggestedStartLocation(PickerLocationId::DocumentsLibrary);

// (Optional) specify the text displayed on commit button. If not specified, use system default:
openPicker.CommitButtonText(L"Choose selected files");

// (Optional) specify file extensions filters. If not specified, default to all (*.*)
openPicker.FileTypeFilter().Append(L".txt");
openPicker.FileTypeFilter().Append(L".pdf");
openPicker.FileTypeFilter().Append(L".doc");
openPicker.FileTypeFilter().Append(L".docx");
```

## FileOpenPicker.PickSingleFilesAsync

Displays a UI element that allows user to choose and open one file.

### Definition
```cpp
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile> PickSingleFileAsync();
```
Return null if the file dialog was cancelled or closed without selection.

### Examples

C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var openPicker = new FileOpenPicker(this.AppWindow.Id);

var file = await openPicker.PickSingleFileAsync();
if (file is not null)
{
var content = System.IO.File.ReadAllText(file.Path);
}
else
{
// error handling.
}
```

C++
```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
#include <fstream>
#include <string>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileOpenPicker openPicker(AppWindow().Id());
auto& file = co_await openPicker.PickSingleFileAsync();
if (file != nullptr)
{
std::ifstream fileReader(file.Path().c_str());
std::string text((std::istreambuf_iterator<char>(fileReader)), std::istreambuf_iterator<char>());
winrt::hstring hText = winrt::to_hstring(text);
}
else
{
// error handling.
}
```

## FileOpenPicker.PickMultipleFilesAsync

Displays a UI element that allows user to choose and open multiple files.

### Definition
```cpp
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Foundation::Collections::IVectorView<winrt::Windows::Storage::StorageFile>> PickMultipleFilesAsync();
```
Return an empty list (Count = 0) if the file dialog's cancelled or closed.

### Examples

C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var openPicker = new FileOpenPicker(this.AppWindow.Id);

var files = await openPicker.PickMultipleFilesAsync();
if (files is not null)
{
var pickedFilePaths = files.Select(f => f.Path);
foreach (var path in pickedFilePaths)
{
var content = System.IO.File.ReadAllText(path);
}
}
else
{
// error handling.
}
```

C++
```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
#include <fstream>
#include <string>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileOpenPicker openPicker(AppWindow().Id());
auto& files = co_await openPicker.PickMultipleFilesAsync();
if (files.Size() > 0)
{
for (auto const& file : files)
{
std::ifstream fileReader(file.Path().c_str());
std::string text((std::istreambuf_iterator<char>(fileReader)), std::istreambuf_iterator<char>());
winrt::hstring hText = winrt::to_hstring(text);
}
}
else
{
// error handling.
}
```
125 changes: 125 additions & 0 deletions specs/StoragePickers/FileSavePicker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
FileSavePicker Class
===

# Background

Namespace: [Microsoft.Windows.Storage.Pickers](./Microsoft.Windows.Storage.Pickers.md)

Represents a UI element that lets the user choose a file to save.

# API Pages

## Constructor

### Attributes

| **Attribute** | **Type** | **Description** |
|----------------------------|--------------------|-------------------|
| `ViewMode` | [Microsoft::Windows::Storage::Pickers::PickerViewMode](./PickerViewMode.md) | Gets or sets the view mode that the file picker is using to present items. |
| `SuggestedStartLocation` | [Microsoft::Windows::Storage::Pickers::PickerLocationId](./PickerLocationId.md)| Gets or sets the initial location where the file picker looks for files. |
| `SuggestedFileName` | `winrt::hstring` | Gets or sets the file name displayed in the file name input box on launching the dialog. |
| `DefaultFileExtension` | `winrt::hstring` | Gets or sets the file extension tailing the suggested file name in the file name input box on launching the dialog. |
| `CommitButtonText` | `winrt::hstring` | Gets or sets the text displayed on the commit button of the file picker. |
| `FileTypeChoices` | `winrt::Windows::Foundation::Collections::IMap<hstring, winrt::Windows::Foundation::Collections::IVector<hstring>>` | The file extensions categorized by purpose.|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why 'choices'?

Windows.Storage.Pickers.FileOpenPicker calls it FileTypeFilter.

IFileDialog and WinForms call it Extension. Haven't looked up WPF and others naming. Windows. WinRT naming seems a good fit. Why invent new terminology?

SUGGESTION: Rename FileTypeChoices to FileTypeFilters



### Examples
C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var savePicker = new FileSavePicker(this.AppWindow.Id)
{
// (Optional) specify the initial location. If not specified, use system default:
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,

// (Optional) specify the default file name. If not specified, use system default:
SuggestedFileName = "My Document",

// (Optional) specify the text displayed on commit button. If not specified, use system default:
CommitButtonText = "Save Document",

// (Optional) categorized extensions types. If not specified, use system default: All Files (*.*)
FileTypeChoices = {
{ "Documents", new List<string> { ".txt", ".doc", ".docx" } }
},

// (Optional) specify the default file extension (will be appended after the default file name).
// If not specified, will not appended after the default extension.
DefaultFileExtension = ".txt",
};
```

C++

```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileSavePicker savePicker(AppWindow().Id());

// (Optional) specify the initial location. If not specified, use system default:
savePicker.SuggestedStartLocation(PickerLocationId::DocumentsLibrary);

// (Optional) specify the default file name. If not specified, use system default:
savePicker.SuggestedFileName(L"NewDocument");

// (Optional) categorized extensions types. If not specified, use system default: All Files (*.*)
savePicker.FileTypeChoices().Insert(L"Text", winrt::single_threaded_vector<winrt::hstring>({ L".txt" }));

// (Optional) specify the default file extension (will be appended after the default file name).
// If not specified, will not appended after the default extension.
savePicker.DefaultFileExtension(L".txt");
```

## FileSavePicker.PickSaveFileAsync

Displays a UI element that allows the user to configure the file path to save.

### Definition
```cpp
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFile> PickSaveFileAsync();
```
Return null if the file dialog was cancelled or closed without selection.

### Examples

C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var savePicker = new FileSavePicker(this.AppWindow.Id);
var file = await savePicker.PickSaveFileAsync();
if (file is not null)
{
System.IO.File.WriteAllText(file.Path, "Hello world.");
}
else
{
// error handling.
}
```

C++

```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
#include <fstream>
#include <string>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FileSavePicker savePicker(AppWindow().Id());
StorageFile file& = co_await savePicker.PickSaveFileAsync();
if (file != nullptr)
{
std::ofstream outFile(file.Path().c_str());
outFile << "Hello world.";
outFile.close();
}
else
{
// error handling.
}
```
100 changes: 100 additions & 0 deletions specs/StoragePickers/FolderPicker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
FolderPicker Class
===

# Background

Namespace: [Microsoft.Windows.Storage.Pickers](./Microsoft.Windows.Storage.Pickers.md)

Represents a UI element that lets the user choose a folder.

Supports specifying the initial location and text on commit button.

# API Pages

## Constructor

### Attributes

| **Attribute** | **Type** | **Description** |
|----------------------------|--------------------|-------------------|
| `ViewMode` | [Microsoft::Windows::Storage::Pickers::PickerViewMode](./PickerViewMode.md) | Gets or sets the view mode that the file picker is using to present items. |
| `SuggestedStartLocation` | [Microsoft::Windows::Storage::Pickers::PickerLocationId](./PickerLocationId.md)| Gets or sets the initial location where the file picker looks for files. |
| `CommitButtonText` | `winrt::hstring` | Gets or sets the text displayed on the commit button of the file picker. |

### Examples

C#

```csharp
using Microsoft.Windows.Storage.Pickers;

var folderPicker = new FolderPicker(this.AppWindow.Id)
{
// (Optional) specify the initial location. If not specified, use system default:
SuggestedStartLocation = PickerLocationId.DocumentsLibrary,

// (Optional) specify the text displayed on commit button. If not specified, use system default:
CommitButtonText = "Select Folder",
};
```

C++

```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FolderPicker folderPicker(AppWindow().Id());

// (Optional) specify the initial location. If not specified, use system default:
folderPicker.SuggestedStartLocation(PickerLocationId::DocumentsLibrary);

// (Optional) specify the text displayed on commit button. If not specified, use system default:
folderPicker.CommitButtonText(L"Select Folder");
```

## FolderPicker.PickSingleFolderAsync

Displays a UI element that allows the user to choose a folder.

### Definition
```cpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WinRT APIs should be defined as MIDL3.

Examples are written in specific languages (usually C#). Non-WinRT APIs are defined in their specific language, in the rare event a WinAppSDK API isn't WinRT.

RECOMMEND: Define as MIDL3, not any specific language

namespace Windows.Storage.Pickers
{
    Windows.Foundation.IAsyncOperation<Windows.Storage.StorageFolder> PickSingleFolderAsync();
}

winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Storage::StorageFolder> PickSingleFolderAsync();
```
Return null if the file dialog was cancelled or closed without selection.

Comment on lines +64 to +65
Copy link
Preview

Copilot AI Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment should be 'Returns null if the file dialog was cancelled or closed without selection.'

Suggested change
Return null if the file dialog was cancelled or closed without selection.
Returns null if the file dialog was cancelled or closed without selection.

Copilot is powered by AI, so mistakes are possible. Review output carefully before use.

Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
### Examples

C#

```csharp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think c# is preferable to csharp as it's supported by more environments (GitHub, ADO, VSCode, ...)

@codendone do you recall that conversation we had. Did it end with c# is the best choice or am I misremembering?

same comment applies throughout

using Microsoft.Windows.Storage.Pickers;

var folderPicker = new FolderPicker(this.AppWindow.Id);
var folder = await folderPicker.PickSingleFolderAsync();
if (folder is not null)
{
var path = folder.Path;
}
else
{
// error handling.
}
```

C++
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You provided a C# example. Why also a C++ example?

The norm is a C# example and only add other languages if they pose additional or different needs beyond mere syntax. Here' the C# and C++ examples are the same just language syntax differences.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @DrusTheAxe

The reason for including both C# and C++ examples in the documentation is to cater to a broader audience. While the examples may only differ in syntax, providing both versions can help developers who are more comfortable with one language over the other.

This approach is not unique to our specs. We have several other documents that provide examples in both languages, such as the CameraCaptureUI.md

```cpp
#include <winrt/Microsoft.Windows.Storage.Pickers.h>
using namespace winrt::Microsoft::Windows::Storage::Pickers;

FolderPicker folderPicker(AppWindow().Id());
auto& folder = co_await folderPicker.PickSingleFolderAsync();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto& folder{ co_await... };

ES.23: Prefer the {}-initializer syntax

Same comments applies throughout

if (folder != nullptr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Examples should use Modern C++

if (!folder)

{
auto path = folder.Path();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto path{ folder.Path() };

ES.23: Prefer the {}-initializer syntax

}
else
{
// error handling.
}
```
Loading