Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
In questo argomento viene illustrato come creare un'applicazione C# .NET di base con funzionalità di interoperabilità WinUI 3 e Win32 usando Platform Invocation Services (PInvoke).
Prerequisiti
App C#/.NET gestita di base
Per questo esempio, si specificheranno la posizione e le dimensioni della finestra dell'app, convertirla e ridimensionarla per i valori DPI appropriati, disabilitare i pulsanti di riduzione al minimo e ingrandire la finestra e infine eseguire una query sul processo corrente per visualizzare un elenco dei moduli caricati nel processo corrente.
Costruiremo la nostra app di esempio dal modello di applicazione iniziale (vedere Prerequisiti). Vedere anche modelli WinUI 3 in Visual Studio.
File MainWindow.xaml
Con WinUI 3, puoi creare istanze della classe Window
La classe finestra XAML è stata estesa per supportare le finestre desktop, trasformandola in un'astrazione di ognuna delle implementazioni di finestra di basso livello usate dai modelli di app desktop e UWP. In particolare, CoreWindow per UWP e maniglie di finestra (o HWND) per Win32.
Il codice seguente mostra il file MainWindow.xaml dell'app modello iniziale, che usa la classe Window come elemento radice per l'app.
<Window
x:Class="WinUI_3_basic_win32_interop.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinUI_3_basic_win32_interop"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="myButton" Click="myButton_Click">Click Me</Button>
</StackPanel>
</Window>
Configurazione
Per chiamare le API Win32 esportate da
User32.dll
, è possibile usare il generatore di origine C#/Win32 P/Invoke nel progetto di Visual Studio. Fare clic su Strumenti>Gestore pacchetti>NuGet Gestisci pacchetti NuGet per la soluzione... e (nella scheda Sfoglia) cercare Microsoft.Windows.CsWin32. Per altre informazioni, vedere Chiamata di funzioni native dal codice gestito.Facoltativamente, è possibile verificare che l'installazione sia riuscita confermando che Microsoft.Windows.CsWin32 è elencato nel nodoPacchetti> in Esplora soluzioni.
È anche possibile fare doppio clic sul file di progetto dell'applicazione (o fare clic con il pulsante destro del mouse e scegliere Modifica file di progetto) per aprire il file in un editor di testo e verificare che il file di progetto includa ora un nuGet
PackageReference
per "Microsoft.Windows.CsWin32".<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>WinExe</OutputType> <TargetFramework>net8.0-windows10.0.19041.0</TargetFramework> <TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion> <RootNamespace>WinUI_3_basic_win32_interop</RootNamespace> <ApplicationManifest>app.manifest</ApplicationManifest> <Platforms>x86;x64;ARM64</Platforms> <RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers> <PublishProfile>win-$(Platform).pubxml</PublishProfile> <UseWinUI>true</UseWinUI> <EnableMsixTooling>true</EnableMsixTooling> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <Content Include="Assets\SplashScreen.scale-200.png" /> <Content Include="Assets\LockScreenLogo.scale-200.png" /> <Content Include="Assets\Square150x150Logo.scale-200.png" /> <Content Include="Assets\Square44x44Logo.scale-200.png" /> <Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" /> <Content Include="Assets\StoreLogo.png" /> <Content Include="Assets\Wide310x150Logo.scale-200.png" /> </ItemGroup> <ItemGroup> <Manifest Include="$(ApplicationManifest)" /> </ItemGroup> <!-- Defining the "Msix" ProjectCapability here allows the Single-project MSIX Packaging Tools extension to be activated for this project even if the Windows App SDK Nuget package has not yet been restored. --> <ItemGroup Condition="'$(DisableMsixProjectCapabilityAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> <ProjectCapability Include="Msix" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.Windows.CsWin32" Version="0.3.183"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> <PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.1742" /> <PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250205002" /> </ItemGroup> <!-- Defining the "HasPackageAndPublishMenuAddedByProject" property here allows the Solution Explorer "Package and Publish" context menu entry to be enabled for this project even if the Windows App SDK Nuget package has not yet been restored. --> <PropertyGroup Condition="'$(DisableHasPackageAndPublishMenuAddedByProject)'!='true' and '$(EnableMsixTooling)'=='true'"> <HasPackageAndPublishMenu>true</HasPackageAndPublishMenu> </PropertyGroup> <!-- Publish Properties --> <PropertyGroup> <PublishReadyToRun Condition="'$(Configuration)' == 'Debug'">False</PublishReadyToRun> <PublishReadyToRun Condition="'$(Configuration)' != 'Debug'">True</PublishReadyToRun> <PublishTrimmed Condition="'$(Configuration)' == 'Debug'">False</PublishTrimmed> <PublishTrimmed Condition="'$(Configuration)' != 'Debug'">True</PublishTrimmed> </PropertyGroup> </Project>
Aggiungere un file di testo al progetto e denominarlo
NativeMethods.txt
. Il contenuto di questo file informa il generatore di codice sorgente C#/Win32 P/Invoke sulle funzioni e i tipi per i quali si desidera generare il codice sorgente P/Invoke. In altre parole, quali funzioni e tipi verranno chiamati e usati nel codice C#.GetDpiForWindow GetWindowLong SetWindowPos SetWindowLong HWND_TOP WINDOW_STYLE
Codice
App.xaml.cs
Nel file di code-behind, si ottiene un handle alla Window utilizzando il metodo di interoperabilità WinRT COM WindowNative.GetWindowHandle (vedere Recuperare un handle di finestra (HWND)).Questo metodo viene chiamato dal gestore OnLaunched dell'app, come illustrato di seguito:
/// <summary> /// Invoked when the application is launched. /// </summary> /// <param name="args">Details about the launch request and process.</param> protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { m_window = new MainWindow(); var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(m_window); SetWindowDetails(hwnd, 800, 600); m_window.Activate(); }
Viene quindi chiamato un metodo
SetWindowDetails
, passando l'handle della finestra e le dimensioni preferite.In questo metodo:
- Chiamiamo GetDpiForWindow per ottenere il valore dei punti per pollice (dpi) per la finestra (Win32 usa pixel fisici, mentre WinUI 3 usa pixel effettivi). Questo valore dpi viene usato per calcolare il fattore di scala e applicarlo alla larghezza e all'altezza specificata per la finestra.
- Chiamiamo quindi SetWindowPos per specificare la posizione desiderata della finestra.
- Infine, chiamiamo SetWindowLong per disabilitare i pulsanti Riduci a icona e Ingrandisci finestra.
private static void SetWindowDetails(IntPtr hwnd, int width, int height) { var dpi = Windows.Win32.PInvoke.GetDpiForWindow((Windows.Win32.Foundation.HWND)hwnd); float scalingFactor = (float)dpi / 96; width = (int)(width * scalingFactor); height = (int)(height * scalingFactor); _ = Windows.Win32.PInvoke.SetWindowPos((Windows.Win32.Foundation.HWND)hwnd, Windows.Win32.Foundation.HWND.HWND_TOP, 0, 0, width, height, Windows.Win32.UI.WindowsAndMessaging.SET_WINDOW_POS_FLAGS.SWP_NOMOVE); var nIndex = Windows.Win32.PInvoke.GetWindowLong((Windows.Win32.Foundation.HWND)hwnd, Windows.Win32.UI.WindowsAndMessaging.WINDOW_LONG_PTR_INDEX.GWL_STYLE) & ~(int)Windows.Win32.UI.WindowsAndMessaging.WINDOW_STYLE.WS_MINIMIZEBOX & ~(int)Windows.Win32.UI.WindowsAndMessaging.WINDOW_STYLE.WS_MAXIMIZEBOX; _ = Windows.Win32.PInvoke.SetWindowLong((Windows.Win32.Foundation.HWND)hwnd, Windows.Win32.UI.WindowsAndMessaging.WINDOW_LONG_PTR_INDEX.GWL_STYLE, nIndex); }
Nel file MainWindow.xaml viene usato un ContentDialog
con un ScrollViewer per visualizzare un elenco di tutti i moduli caricati per il processo corrente.<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center"> <Button x:Name="myButton" Click="myButton_Click">Display loaded modules</Button> <ContentDialog x:Name="contentDialog" CloseButtonText="Close"> <ScrollViewer> <TextBlock x:Name="cdTextBlock" TextWrapping="Wrap" /> </ScrollViewer> </ContentDialog> </StackPanel>
Sostituire quindi il gestore eventi
MyButton_Click
con il codice seguente.In questo caso si ottiene un riferimento al processo corrente chiamando GetCurrentProcess. Si scorre l'insieme di Modules quindi si aggiunge il nome del file di ogni ProcessModule alla stringa da visualizzare.
private async void myButton_Click(object sender, RoutedEventArgs e) { myButton.Content = "Clicked"; var description = new System.Text.StringBuilder(); var process = System.Diagnostics.Process.GetCurrentProcess(); foreach (System.Diagnostics.ProcessModule module in process.Modules) { description.AppendLine(module.FileName); } cdTextBlock.Text = description.ToString(); await contentDialog.ShowAsync(); }
Compilare ed eseguire l'app.
Dopo aver visualizzato la finestra, selezionare il pulsante "Visualizza moduli caricati".
L'applicazione di interoperabilità Win32 di base descritta in questo argomento.
Riassunto
In questo argomento è stato illustrato l'accesso all'implementazione della finestra sottostante (in questo caso Win32 e HWND) e all'uso delle API Win32 insieme alle API WinRT. In questo modo viene illustrato come usare il codice dell'applicazione desktop esistente durante la creazione di nuove app desktop WinUI 3.
Per un esempio più completo, vedere l'esempio della raccolta
Esempio di personalizzazione della barra del titolo della finestra
In questo secondo esempio viene illustrato come personalizzare la barra del titolo della finestra e il relativo contenuto. Prima di procedere con esso, esaminare questi argomenti:
Creare un nuovo progetto
- In Visual Studio creare un nuovo progetto C# o C++/WinRT dal modello di progetto App vuota, In pacchetto (WinUI 3 in Desktop).
Configurazione
Anche in questo caso, fare riferimento al pacchetto NuGet Microsoft.Windows.CsWin32 esattamente come è stato fatto nel primo esempio.
Aggiungere un file di
NativeMethods.txt
testo al progetto.LoadImage SendMessage SetWindowText WM_SETICON
MainWindow.xaml
Annotazioni
Se è necessario un file di icona da usare con questa procedura dettagliata, è possibile scaricare il computer.ico
file dall'app di esempio WirelessHostednetwork . Posiziona il file nella tua cartella Assets
, e aggiungi il file al tuo progetto come contenuto. Sarà quindi possibile fare riferimento al file usando l'URL Assets/computer.ico
.
In caso contrario, è possibile usare un file di icona già presente e modificare i due riferimenti nell'elenco di codice riportato di seguito.
- Nell'elenco di codice riportato di seguito si noterà che in
MainWindow.xaml
sono stati aggiunti due pulsanti e i gestori di clic specificati per ognuno. Nel gestore Clic per il primo pulsante (basicButton_Click), impostiamo l'icona e il testo della barra del titolo. Nel secondo (customButton_Click), dimostriamo una personalizzazione più significativa sostituendo la barra del titolo con il contenuto di StackPanel denominato customTitleBarPanel.
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="window_titlebar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:window_titlebar"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Basic WinUI 3 Window title bar sample">
<Grid x:Name="rootElement" RowDefinitions="100, *, 100, *">
<StackPanel x:Name="customTitleBarPanel" Grid.Row="0" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Top" Visibility="Collapsed">
<Image Source="Images/windowIcon.gif" />
<TextBlock VerticalAlignment="Center" Text="Full customization of title bar"/>
</StackPanel>
<StackPanel x:Name="buttonPanel" Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="basicButton" Click="basicButton_Click" Margin="25">Set the Window title and icon</Button>
<Button x:Name="customButton" Click="customButton_Click" Margin="25">Customize the window title bar</Button>
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs/cpp
- Nell'elenco di codice riportato di seguito per il gestore basicButton_Click, per mantenere nascosta la barra del titolo personalizzata, comprimiamo customTitleBarPanel StackPanel e impostiamo la proprietà ExtendsContentIntoTitleBar su
false
. - Chiamiamo quindi IWindowNative::get_WindowHandle (per C#, usando il metodo helper di interoperabilità GetWindowHandle) per recuperare l'handle della finestra (HWND) della finestra principale.
- Successivamente, impostiamo l'icona dell'applicazione (per C#, usando il pacchetto NuGet PInvoke.User32 ) chiamando le funzioni LoadImage e SendMessage .
- Infine, chiamiamo SetWindowText per aggiornare la stringa della barra del titolo.
private void basicButton_Click(object sender, RoutedEventArgs e)
{
// Ensure the custom title bar content is not displayed.
customTitleBarPanel.Visibility = Visibility.Collapsed;
// Disable custom title bar content.
ExtendsContentIntoTitleBar = false;
//Get the Window's HWND
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var hIcon = Windows.Win32.PInvoke.LoadImage(
null,
"Images/windowIcon.ico",
Windows.Win32.UI.WindowsAndMessaging.GDI_IMAGE_TYPE.IMAGE_ICON,
20, 20,
Windows.Win32.UI.WindowsAndMessaging.IMAGE_FLAGS.LR_LOADFROMFILE);
Windows.Win32.PInvoke.SendMessage(
(Windows.Win32.Foundation.HWND)hwnd,
Windows.Win32.PInvoke.WM_SETICON,
(Windows.Win32.Foundation.WPARAM)0,
(Windows.Win32.Foundation.LPARAM)hIcon.DangerousGetHandle());
Windows.Win32.PInvoke.SetWindowText((Windows.Win32.Foundation.HWND)hwnd, "Basic customization of title bar");
}
// pch.h
...
#include <microsoft.ui.xaml.window.h>
...
// MainWindow.xaml.h
...
void basicButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
...
// MainWindow.xaml.cpp
void MainWindow::basicButton_Click(IInspectable const&, RoutedEventArgs const&)
{
// Ensure the that custom title bar content is not displayed.
customTitleBarPanel().Visibility(Visibility::Collapsed);
// Disable custom title bar content.
ExtendsContentIntoTitleBar(false);
// Get the window's HWND
auto windowNative{ this->m_inner.as<::IWindowNative>() };
HWND hWnd{ 0 };
windowNative->get_WindowHandle(&hWnd);
HICON icon{ reinterpret_cast<HICON>(::LoadImage(nullptr, L"Assets/computer.ico", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE)) };
::SendMessage(hWnd, WM_SETICON, 0, (LPARAM)icon);
this->Title(L"Basic customization of title bar");
}
- Nel gestore customButton_Click impostiamo la visibilità di customTitleBarPanel StackPanel su Visible.
- Impostiamo quindi la proprietà ExtendsContentIntoTitleBar su
true
e chiamiamo SetTitleBar per visualizzare customTitleBarPanel StackPanel come barra del titolo personalizzata.
private void customButton_Click(object sender, RoutedEventArgs e)
{
customTitleBarPanel.Visibility = Visibility.Visible;
// Enable custom title bar content.
ExtendsContentIntoTitleBar = true;
// Set the content of the custom title bar.
SetTitleBar(customTitleBarPanel);
}
// MainWindow.xaml.h
...
void customButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args);
...
// MainWindow.xaml.cpp
void MainWindow::customButton_Click(IInspectable const&, RoutedEventArgs const&)
{
customTitleBarPanel().Visibility(Visibility::Visible);
// Enable custom title bar content.
ExtendsContentIntoTitleBar(true);
// Set the content of the custom title bar.
SetTitleBar(customTitleBarPanel());
}
App.xaml
-
App.xaml
Nel file, subito dopo il<!-- Other app resources here -->
commento, sono stati aggiunti alcuni pennelli colorati personalizzati per la barra del titolo, come illustrato di seguito.
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="window_titlebar.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:window_titlebar">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
<!-- Other merged dictionaries here -->
</ResourceDictionary.MergedDictionaries>
<!-- Other app resources here -->
<SolidColorBrush x:Key="WindowCaptionBackground">Green</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionBackgroundDisabled">LightGreen</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionForeground">Red</SolidColorBrush>
<SolidColorBrush x:Key="WindowCaptionForegroundDisabled">Pink</SolidColorBrush>
</ResourceDictionary>
</Application.Resources>
</Application>
Se hai seguito questi passaggi nella tua app, puoi compilare ora il progetto ed eseguire l'app. Verrà visualizzata una finestra dell'applicazione simile alla seguente (con l'icona dell'app personalizzata):
Modello di app.
Ecco la barra del titolo personalizzata di base:
App modello con icona app personalizzata.Ecco la barra del titolo completamente personalizzata:
App modello con barra del titolo personalizzata.