Introduction
Task statement: To select a file to open from the WinUI 3 desktop application.
It turned out to find the class Windowss.Storage.Pickers.FileOpenPicker, setting up options and calling the PickSingleFileAsync() method is not enough.
The following code:
// Create a file picker
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
// Set options for your file picker
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.FileTypeFilter.Add("*");
// Open the picker for the user to pick a file
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
// File picked up
}
else
{
// Something went wrong
}
Causes an error. Something like this:

After that, I had to start to understand the issue seriously.
General information
In Windows User Interface 3 (WinUI 3), in addition to the Page (Page), Frame (Frame), the concept of Window (Window) was added. And you can work with windows (at the same time, you can create several of them) and you can continue to work with pages out of habit from UWP. Panels (Panel) can be placed both inside the window and inside the page (postarinke).
WinUI is completely written in C++ and can be used in unmanaged Windows applications. (Along the way, I had a question: why then did I need to screw up C#, if we return to C++). WinUI 3 is part of the Windows App SDK.
If we want to work with pages, then the Window class contains a root page in the Content attribute, for example, a Shell for working with the application (well, or as it is written in the documentation – the visual root of the application window).
The Windows namespace.Storage.Pickers provides classes for creating and managing user interface elements that allow the user to view files, select them to open and save, and select the name, extension, and location of files.
In UWP, the code above works fine. For the code to work in WinUI 3, it is necessary to bind an instance of the FileOpenPicker object with the window handle (handler – HWND) of the corresponding window in the operating system. To do this, we need to perform two actions:
1. Get the handle of the current window.
2. Associate an instance of the file selection object with the received descriptor.
Microsoft recommends the following code for this:
// 1. Retrieve the window handle (HWND) of the current WinUI 3 window.
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
где window – это экземпляр окна в котором мы хотим выполнить диалог выбора файла.
// 2. Initialize the folder picker with the window handle (HWND).
WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);
It seems to have figured everything out, but there is one more interesting question: where to get this most current window, especially in multi-window applications?
To answer this question, let's consider two options: one from a comprehensive example from Microsoft with github, the second from the Template Studio for WinUI (C#) template.
Analysis of the example from Microsoft
An example from Microsoft showing the capabilities of WinUI3 is in github: microsoft/Wine-Gallery (https://github.com/microsoft/WinUI-Gallery/tree/main).
The example implies working with multiple windows, so preparing for this looks a little more complicated. To solve the problem of determining the current window, a special assistant is used.
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************
using Microsoft.UI.Xaml;
using System.Collections.Generic;
namespace AppUIBasics.Helper
{
// Helper class to allow the app to find the Window that contains an
// arbitrary UIElement (GetWindowForElement). To do this, we keep track
// of all active Windows. The app code must call WindowHelper.CreateWindow
// rather than "new Window" so we can keep track of all the relevant
// windows. In the future, we would like to support this in platform APIs.
public class WindowHelper
{
static public Window CreateWindow()
{
Window newWindow = new Window();
TrackWindow(newWindow);
return newWindow;
}
static public void TrackWindow(Window window)
{
window.Closed += (sender,args) => {
_activeWindows.Remove(window);
};
_activeWindows.Add(window);
}
static public Window GetWindowForElement(UIElement element)
{
if (element.XamlRoot != null)
{
foreach (Window window in _activeWindows)
{
if (element.XamlRoot == window.Content.XamlRoot)
{
return window;
}
}
}
return null;
}
static public UIElement FindElementByName(UIElement element, string name)
{
if (element.XamlRoot != null && element.XamlRoot.Content != null)
{
var ele = (element.XamlRoot.Content as FrameworkElement).FindName(name);
if (ele != null)
{
return ele as UIElement;
}
}
return null;
}
static public List<Window> ActiveWindows { get { return _activeWindows; }}
static private List<Window> _activeWindows = new List<Window>();
}
}
The WindowHelper class contains the full ActiveWindow s property, which contains a list of currently active windows and the following methods:
CreateWindow() – creates a new window and adds it to the list of active windows.
TrackWindow(Window) – adds a window to the active list and connects it to the window closing event, removing the window from the active list.
GetWindowForElement(UIElement) – allows you to get a window for the current control.
FindElementByName(UIElement, string) – allows you to find the control by name.
A window is created in the App.xaml.cs file:
/// <summary>
/// Invoked when the application is launched normally by the end user.
/// Other entry points
/// will be used such as when the application is launched to open a specific file.
/// </summary>
/// <param name="e">Details about the launch request and process.</param>
protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
{
IdleSynchronizer.Init();
startupWindow = WindowHelper.CreateWindow();
startupWindow.ExtendsContentIntoTitleBar = true;
···
Next, in the country file FilePicker Page.xaml.cs, we get the current window:
// Create a file picker
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
// Retrieve the window handle (HWND) of the current WinUI 3 window.
var window = WindowHelper.GetWindowForElement(this);
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(window);
// Initialize the file picker with the window handle (HWND).
WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);
// Set options for your file picker
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.FileTypeFilter.Add("*");
// Open the picker for the user to pick a file
var file = await openPicker.PickSingleFileAsync();
if (file != null)
{
// Creating the text for the Texblock
Span span = new Span();
Run run1 = new Run();
run1.Text = "Picked file: ";
// Adding the name of the picked file in bold
Run run2 = new Run();
run2.FontWeight = Microsoft.UI.Text.FontWeights.Bold;
run2.Text = file.Name;
span.Inlines.Add(run1);
span.Inlines.Add(run2);
PickAFileOutputTextBlock.Inlines.Add(span);
}
else
{
PickAFileOutputTextBlock.Text = "Operation cancelled.";
}
Usage in TS WinUI
The template studio uses only one window, it is stored in the static MainWindow property in the App:Application class in the App.xaml.cs file.
Anywhere in the application we can refer to it App.MainWindow. And then our code will look like
// Create a file picker
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
// Retrieve the window handle (HWND) of the current WinUI 3 window.
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
// Initialize the file picker with the window handle (HWND).
WinRT.Interop.InitializeWithWindow.Initialize(openPicker, hWnd);
// Set options for your file picker
openPicker.ViewMode = PickerViewMode.Thumbnail;
openPicker.FileTypeFilter.Add("*");
// Open the picker for the user to pick a file
var file = await openPicker.PickSingleFileAsync();
Conclusion
These are the changes I had to make in my application and learn something new.
I hope this information will be useful to you.
17.02.2023
P.S.
Для отображения диалогового окна используем точно такой же подход:
catch (Exception ex)
{
var msgDialog = new MessageDialog(
$"ERROR when selecting a file: {ex.Message}.",
" Selecting a file to open ");
// Retrieve the window handle (HWND) of the current WinUI 3 window.
var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
// Initialize the file picker with the window handle (HWND).
WinRT.Interop.InitializeWithWindow.Initialize(msgDialog, hWnd);
var result = await msgDialog.ShowAsync();
}
Additions:
Addendum 1:
To select a file , you must specify at least one file type filter .FileTypeFilterr, valid format is only ".txt" or "*", any other will cause an error.
Keywords: Winui 3, Windows User Interface 3, Windows.Storage.Picker, Picksinglefileasync, File selection in WinUI 3.
Add new comment