How to allow picking files from other apps in a file picker in WPF and Windows Forms/WinForms in C# and VB

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:
Windows file picker with apps showing in the navigation pane

...and here is one without the options to pick from other apps:
Windows file picker without apps showing in the navigation pane


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)


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(); 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:

Using the Camera app to pick a file in a Windows file picker

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. 

Installing Microsoft.Windows.SDK.Contracts in Visual Studio

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:

Using the Camera app to pick a file in a Windows file picker

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

Popular posts from this blog

How to show placeholder text in a WPF TextBox in C# and VB

How to enable dark title bar in WPF and Windows Forms/WinForms apps in C# and VB

How to use modern icons in XAML in WPF on Windows 10 and 11

How to change the colour of a WPF or Windows Forms/WinForms title bar in Windows 11 in C# and VB

Microsoft WebView2: How to check if the WebView2 runtime is installed in C# and VB