Vytvoření aplikace .NET v C# s využitím zprostředkovatele WinUI 3 a Win32
V tomto tématu si projdeme postup vytvoření základní aplikace C# .NET s funkcemi komunikace WinUI 3 a Win32 pomocí služeb volání platformy (PInvoke).
Požadavky
Základní spravovaná aplikace C#/.NET
V tomto příkladu určíme umístění a velikost okna aplikace, převedeme ho a škálujeme podle příslušného DPI, zakážeme okno minimalizovat a maximalizovat tlačítka a nakonec provedeme dotaz na aktuální proces, abychom zobrazili seznam modulů načtených do aktuálního procesu.
Vytvoříme naši ukázkovou aplikaci z výchozí aplikace šablony (viz Požadavky). Podívejte se také na šablony WinUI 3 v sadě Visual Studio.
Soubor MainWindow.xaml
S WinUI 3 můžete vytvořit instance Window třídy v kódu XAML.
Třída Okna XAML byla rozšířena tak, aby podporovala desktopová okna a přeměnila ji na abstrakci každé z implementací oken nízké úrovně, které používají modely UPW a desktopových aplikací. Konkrétně CoreWindow pro UWP a úchyty oken (nebo HWND) pro Win32.
Následující kód ukazuje soubor MainWindow.xaml z výchozí šablonové aplikace, který používá třídu Window jako kořenový prvek aplikace.
<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>
Konfigurace
K volání rozhraní API Win32 exportovaných z
User32.dll
můžete použít C#/Win32 P/Invoke Source Generator v projektu sady Visual Studio. Klikněte na Nástroje>Správce balíčků NuGet>Spravovat balíčky NuGet pro řešení...a (na kartě Procházet) vyhledejte Microsoft.Windows.CsWin32. Další podrobnosti najdete v tématu Volání nativních funkcí ze spravovaného kódu.Volitelně můžete ověřit, že instalace proběhla úspěšně, kontrolou, že Microsoft.Windows.CsWin32 je uveden pod uzlem Závislosti>Balíčky v Průzkumníku řešení.
Volitelně můžete také dvakrát kliknout na soubor projektu aplikace (nebo kliknout pravým tlačítkem myši a vybrat Upravit soubor projektu) a otevřít soubor v textovém editoru a potvrdit, že soubor projektu teď obsahuje nuGet
PackageReference
pro "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>
Přidejte do projektu textový soubor a pojmenujte ho
NativeMethods.txt
. Obsah tohoto souboru informuje C#/Win32 P/Invoke Source Generator o funkcích a typech, pro které chcete vygenerovat zdrojový kód P/Invoke. Jinými slovy, které funkce a typy budete volat a používat v kódu jazyka C#.GetDpiForWindow GetWindowLong SetWindowPos SetWindowLong HWND_TOP WINDOW_STYLE
Kód
V souboru
App.xaml.cs
kódem získáme popisovač okna pomocí metody WindowNative.GetWindowHandle WinRT COM komunikace (viz Načtení úchytu okna (HWND)).Tato metoda se volá z obslužné rutiny OnLaunched aplikace, jak je znázorněno zde:
/// <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(); }
Poté zavoláme metodu
SetWindowDetails
, přičemž předáme popisovač okna a upřednostňované rozměry.V této metodě:
- Voláme GetDpiForWindow k získání bodů na palec (dpi) pro okno (Win32 používá fyzické pixely, zatímco WinUI 3 používá efektivní pixely). Tato hodnota dpi se používá k výpočtu měřicího faktoru a jeho aplikaci na zadanou šířku a výšku okna.
- Potom zavoláme SetWindowPos určit požadované umístění okna.
- Nakonec zavoláme SetWindowLong, abychom zakázali tlačítka Minimalizovat a Maximalizovat.
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); }
V souboru MainWindow.xaml používáme ContentDialog s ScrollViewer k zobrazení seznamu všech modulů načtených pro aktuální proces.
<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>
Potom nahradíme obslužnou rutinu události
MyButton_Click
následujícím kódem.Zde získáme odkaz na aktuální proces voláním GetCurrentProcess. Poté procházíme kolekci Modulů a k našemu zobrazovacímu řetězci připojíme název souboru každého ProcessModule.
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(); }
Zkompilujte a spusťte aplikaci.
Po zobrazení okna vyberte tlačítko Zobrazit načtené moduly.
Základní aplikace vzájemné spolupráce Win32 popsaná v tomto tématu.
Shrnutí
V tomto tématu jsme probrali přístup k implementaci podkladového okna (v tomto případě Win32 a HWND) a použití rozhraní API Win32 společně s rozhraními API WinRT. Ukazuje, jak můžete při vytváření nových desktopových aplikací WinUI 3 použít stávající kód desktopové aplikace.
Rozsáhlejší ukázku najdete v ukázce galerie AppWindow v ukázkách sady Windows App SDK úložišti GitHub.
Příklad přizpůsobení záhlaví okna
V tomto druhém příkladu si ukážeme, jak přizpůsobit záhlaví okna a jeho obsah. Než podle něj postupujete, projděte si tato témata:
Vytvoření nového projektu
- V sadě Visual Studio vytvořte nový projekt C# nebo C++/WinRT ze šablony projektu Prázdná aplikace, zabalená (WinUI 3 pro stolní počítače).
Konfigurace
Odkazujte na Microsoft.Windows.CsWin32 balíček NuGet stejně, jako jsme to udělali v prvním příkladu.
Přidejte do projektu
NativeMethods.txt
textový soubor.LoadImage SendMessage SetWindowText WM_SETICON
MainWindow.xaml
Poznámka:
Pokud potřebujete soubor ikony pro použití s tímto návodem, můžete si stáhnout soubor computer.ico
z ukázkové aplikace WirelessHostednet work. Umístěte tento soubor do složky Assets
a přidejte ho do projektu jako obsah. Pak budete moct odkazovat na soubor pomocí adresy URL Assets/computer.ico
.
V opačném případě můžete použít soubor ikony, který už máte, a změnit na něj dva odkazy v níže uvedených výpisech kódu.
- V následujícím výpisu kódu uvidíte, že v
MainWindow.xaml
jsme přidali dvě tlačítka a pro každý z nich jsme zadali klikni obslužné rutiny. V obslužné rutině Click pro první tlačítko (basicButton_Click) nastavíme ikonu záhlaví a text. Ve druhém (customButton_Click) předvádíme významnější úpravu nahrazením titulního pruhu obsahem StackPanel s názvem 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
- V následujícím výpisu kódu pro obslužnou rutinu basicButton_Click – aby bylo vlastní záhlaví skryté – skryjeme customTitleBarPanelStackPanela nastavíme vlastnost ExtendsContentIntoTitleBar na
false
. - Potom voláme IWindowNative::get_WindowHandle (pro jazyk C# pomocí zprostředkovací pomocné metody GetWindowHandle) pro získání popisovače okna (HWND) hlavního okna.
- Dále nastavíme ikonu aplikace (pro C# pomocí balíčku NuGet PInvoke.User32) zavoláním funkcí LoadImage a SendMessage.
- Nakonec zavoláme SetWindowText k aktualizaci řetězce záhlaví.
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");
}
- V obslužné rutině customButton_Click nastavíme viditelnost customTitleBarPanelStackPanel na Viditelné.
- Potom nastavíme vlastnost ExtendsContentIntoTitleBar na
true
a zavoláme SetTitleBar pro zobrazení customTitleBarPanelStackPanel jako našeho vlastního záhlaví.
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
- V souboru
App.xaml
jsme hned za komentář<!-- Other app resources here -->
přidali několik vlastních barevných štětců pro záhlaví, jak je znázorněno níže.
<?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>
Pokud jste postupovali podle těchto kroků ve své vlastní aplikaci, můžete projekt sestavit a spustit aplikaci. Zobrazí se okno aplikace podobné následujícímu (s ikonou vlastní aplikace):
aplikace pro šablony.
Tady je základní vlastní záhlaví:
Šablonová aplikace s vlastní ikonou aplikaceTady je zcela přizpůsobená titulní lišta:
Aplikace šablony s vlastním titulním pruhem.
Viz také
- Windows App SDK
- Kanál stabilního vydání pro Windows App SDK
- Správa oken aplikací
- Ukázky sady Windows App SDK
Windows developer