You may have noticed that sometimes in file pickers in Windows, you not only have the option of picking files from folders, but also have the option of picking files through other apps - like selecting a photo from your photos library using the Photos app or taking a picture with the
Camera app, all right from the file picker. What you may have noticed, however, is that the options to pick from apps don't always show up. For context, here is a file picker that lets you pick from other apps:
...and here is one without the options to pick from other apps:
As you can see, in the second screenshot, the options to pick files using the
Camera or Photos app are gone. As a developer, you might want to show those options as they can be useful - for example, the Photos app compiles all your photos together, making it easy to look through them all instead of having to dig through folders and the
Camera app can be used to allow users to take photos with the camera right from your Windows Forms or WPF app, and this feature is not limited to the Photos and
Camera apps, other apps can add themselves to this list as well.
How to enable picking from other apps in file pickers
So now you might be wondering how to enable this functionality. Well, it's actually not something you can enable/disable. Instead, it actually depends on which API you use to show a file picker and the ones you're likely using in C# or VB - the
System.Windows.Forms.OpenFileDialog or the Microsoft.Win32.OpenFileDialog - do not support this functionality. Instead, you'll need to use the newer
Windows.Storage.Pickers.FileOpenPicker. When you use this class, you will automatically get any apps registered for this functionality showing in the picker window. So here's how to use this class.
Note: This guide is compatible with Windows 10 and above.
Second note: The
Windows.Storage.Pickers.FileOpenPicker class will not work when your app is running as administrator - it will just throw an exception when you try to use it. You might want to check if the app is running as administrator or wrap the code in a try catch and then fall back to another file picker API such as the
System.Windows.Forms.OpenFileDialog or Microsoft.Win32.OpenFileDialog class if necessary.
For .Net 5 and above
If you're using .Net 5 or above, the first thing you'll need to do is to make sure your project is set the target a version of Windows 10. To do this, right click the project and select 'Edit Project File'. This will open your project file in text view. From here, you'll need to specify a target Windows 10 version in the
TargetFramework element under the PropertyGroup element in the format of [.netversion]-windows[windowsversionnumber] as shown in the code below.
The code below specifies Windows 10 version 1809 (The October 2018 update) and .Net 7. Which version you specify depends on compatibility - for best compatibility, specify the lowest version of Windows 10 you want your app to run on as this will give you access to APIs that are compatible with that version of Windows and above. To see the specific build numbers for each major release of Windows 10, see
here and
here for Windows 11 if you want to specify a Windows 11 version instead. The code in this article has been tested with 10.0.17763.0 and above being specified in the target framework. This may seem a little complicated, but it's necessary in .Net 5 and above in order to access the Windows 10 UWP APIs, which we'll be using. If you're confused, we'd recommend just copying and pasting the below code into your project file under the PropertyGroup section, replacing the
TargetFramework element if it already exists and making sure that 'net7.0' is set to the version of .Net you want to target. With this version of Windows specified, your code should work fine on Windows 10 October 2018 Update and above, so long as the specified version of .Net is compatible with it (which
.Net 7 is).
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
What you'll end up with, is something like this (note that this is quite a basic example, you might have more in this file depending on your project):
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net7.0-windows10.0.17763.0</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup>
</Project>
Now, we can access the APIs we need. Now, at the bare minimum, in order to show a
Windows.Storage.Pickers.FileOpenPicker, we need to create the object, set a file type filter and then show the picker, which you can do like so:
C#:
Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add("*");
await picker.PickSingleFileAsync();
VB:
Dim picker As Windows.Storage.Pickers.FileOpenPicker = New Windows.Storage.Pickers.FileOpenPicker()
picker.FileTypeFilter.Add("*")
Await picker.PickSingleFileAsync()
Tip: Don't forget to mark the method/function where you're calling this code as async since we're using await (such as 'private async void' (C#) or Private Async Sub (VB). If you're unsure, Visual Studio will likely help you with this by showing an error and giving an option to fix it by marking the function/method as async.
The above code will show a file picker allowing you to pick from any file type with options for picking from other apps showing in the navigation pane. However, you will likely get an exception such as the following when running this code:
'Invalid window handle. (0x80070578)
Consider WindowNative, InitializeWithWindow
See https://aka.ms/cswinrt/interop#windows-sdk'
Luckily, it's easy to fix - it happens because the file picker doesn't know who its parent window should be. To fix it, we need to use a function called
InitializeWithWindow, which tells the picker who its owner window should be. Here's how to use that function:
C#:
WinRT.Interop.InitializeWithWindow.Initialize(picker, new System.Windows.Interop.WindowInteropHelper(this).Handle);
VB:
WinRT.Interop.InitializeWithWindow.Initialize(picker, New System.Windows.Interop.WindowInteropHelper(Me).Handle)
C#:
Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
WinRT.Interop.InitializeWithWindow.Initialize(picker, new System.Windows.Interop.WindowInteropHelper(this).Handle);
picker.FileTypeFilter.Add("*");
await picker.PickSingleFileAsync();
VB:
Dim picker As Windows.Storage.Pickers.FileOpenPicker = New Windows.Storage.Pickers.FileOpenPicker()
WinRT.Interop.InitializeWithWindow.Initialize(picker, New System.Windows.Interop.WindowInteropHelper(this).Handle)
picker.FileTypeFilter.Add("*")
Await picker.PickSingleFileAsync()
Now, run the code above (or your modified version of the code) and you'll now be able to pick from other apps:
To customise the picker and for more information about how to use it, look
here. You can use code samples from there, but make sure to always include the
WinRT.Interop.InitializeWithWindow.Initialize function as we did above. Also, note that the
PickSingleFileAsync function returns a
StorageFile object (So long as you await it by adding the await keyword right before the function, like we did in the above code samples). To access the path of the file, just use the
Path property of the
StorageFile.
For .Net Framework 4.5 and above and .Net Core 3.0 and 3.1
If you're using .Net Framework 4.5 or above or .Net Core 3.1, follow this instructions here. Note that these instructions aren't compatible with older versions of .Net Framework or .Net Core. So first, we'll need to installed the
Microsoft.Windows.SDK.Contracts NuGet package to access the UWP/WinRT APIs. To do this, right click on your project and select 'Manage NuGet Packages...' then go to the Browse tab, search for 'Microsoft.Windows.SDK.Contracts' and install
Microsoft.Windows.SDK.Contracts. Which version you select depends on compatibility - for best compatibility, select the version that matches lowest version of Windows 10 you want your app to run on as this will give you access to APIs that are compatible with that version of Windows and above. The lowest supported version of Windows that each version of this package is supported on is listed in the description. For this tutorial, we'll be using version 10.0.17134.1000 which is compatible with Windows 10 1803 (April 2018 update) but you can use another version if you want. To see the specific build numbers for each major release of Windows 10, see
here and
here for Windows 11. If you're unsure, just use the same version we're using and your code should work fine on Windows 10 1803 and above.
Now, we can access the APIs we need. Now, at the bare minimum, in order to show a
Windows.Storage.Pickers.FileOpenPicker, we need to create the object, set a file type filter and then show the picker, which you can do like so:
C#:
Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add("*");
await picker.PickSingleFileAsync();
VB:
Dim picker As Windows.Storage.Pickers.FileOpenPicker = New Windows.Storage.Pickers.FileOpenPicker()
picker.FileTypeFilter.Add("*")
Await picker.PickSingleFileAsync()
Tip: Don't forget to mark the method/function where you're calling this code as async since we're using await (such as 'private async void' (C#) or Private Async Sub (VB). If you're unsure, Visual Studio will likely help you with this by showing an error and giving an option to fix it by marking the function/method as async.
The above code will show a file picker allowing you to pick from any file type with options for picking from other apps showing in the navigation pane. However, you will likely get an exception such as the following when running this code:
'Invalid window handle. (0x80070578)
Consider WindowNative, InitializeWithWindow
See https://aka.ms/cswinrt/interop#windows-sdk'
Luckily, it's easy to fix - it happens because the file picker doesn't know who its owner window (the window that the picker should show on top of) should be. To fix it, we need to use an interface called
IInitializeWithWindow, which exposes a function called
Initalize which tells the picker who its parent window should be. We can use the ComImport features to access this interface. Firstly, we need to declare the interface somewhere in our code. You can put the following declaration in the same class where you're calling the file picker, or put it in another class and access it through that class:
C#:
[System.Runtime.InteropServices.ComImport]
[System.Runtime.InteropServices.Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[System.Runtime.InteropServices.InterfaceType(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
VB:
<ComImport>
<Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IInitializeWithWindow
Sub Initialize(ByVal hwnd As IntPtr)
End Interface
Now, we can use this function to tell the picker which window should be its owner. Here's how to do it:
C#:
((IInitializeWithWindow)(object)picker).Initialize(new System.Windows.Interop.WindowInteropHelper(this).Handle);
VB:
Dim iwwPicker As ModernMessageBoxHelper.IInitializeWithWindow = picker
iwwPicker.Initialize(New Interop.WindowInteropHelper(Application.Current.MainWindow).Handle)
The above code casts the FileOpenPicker as an IInitializeWithWindow so that we can call the Initialize function to provide it with a handle (unique identifier) of the window who should be its owner.
Call this code after the creation of the FileOpenPicker object but before you show it. Also, this code assume it's being called from a WPF Window. If it's not, replace this/Me with the Window or Form who should be the picker's parent. If it's a Form, remove System.Windows.Interop.WindowInteropHelper as Forms have a Handle property you can access directly. What you'll end up with, is something like this:
C#:
Windows.Storage.Pickers.FileOpenPicker picker = new Windows.Storage.Pickers.FileOpenPicker();
((IInitializeWithWindow)(object)picker).Initialize(new System.Windows.Interop.WindowInteropHelper(this).Handle);
picker.FileTypeFilter.Add("*");
await picker.PickSingleFileAsync();
VB:
Dim picker As Windows.Storage.Pickers.FileOpenPicker = New Windows.Storage.Pickers.FileOpenPicker()
Dim iwwPicker As ModernMessageBoxHelper.IInitializeWithWindow = picker
iwwPicker.Initialize(New Interop.WindowInteropHelper(Application.Current.MainWindow).Handle)
picker.FileTypeFilter.Add("*")
Await picker.PickSingleFileAsync()
Now, run the code above (or your modified version of the code) and you'll now be able to pick from other apps:
To customise the picker and for more information about how to use it, look
here. You can use code samples from there, but make sure to always include the Initialize function as we did above. Also, note that the
PickSingleFileAsync function returns a
StorageFile object (So long as you await it by adding the await keyword right before the function, like we did in the above code samples). To access the path of the file, just use the
Path property of the
StorageFile.
If you have any questions about this, ask away in the comments! It's a bit more involved to get the Windows.Storage.Pickers.FileOpenPicker working in a Windows Forms or WPF app compared to the System.Windows.Forms.OpenFileDialog and Microsoft.Win32.OpenFileDialog so if anything here is unclear, confusing or doesn't work for you, feel free to ask! Also, if this guide did help you and you use an AdBlocker and you want to support us, consider disabling it for this site - if you do, it will be greatly appreciated!
Follow us to be notified when we release new guides:
Comments
Post a Comment