Aracılığıyla paylaş


WinUI 3 ve Win32 birlikte çalışma ile C# .NET uygulaması oluşturma

Bu konu başlığında, Platform Çağırma Hizmetleri'ni (PInvoke) kullanarak WinUI ve Win32 birlikte çalışma özellikleriyle temel bir C# .NET uygulaması oluşturma adımlarını atacağız.

Prerequisites

  1. Windows uygulamaları geliştirmeye başlama

Temel yönetilen C#/.NET uygulaması

Bu örnekte, uygulama penceresinin konumunu ve boyutunu belirleyecek, uygun DPI için dönüştürüp ölçeklendirecek, pencerenin simge durumuna küçültme ve ekranı kaplama düğmelerini devre dışı bırakacağız ve son olarak, geçerli işleme yüklenen modüllerin listesini göstermek amacıyla geçerli işlemi sorgulayacağız.

Örnek uygulamamızı ilk şablon uygulamasından oluşturacağız (bkz . Önkoşullar). Ayrıca bkz. WinUI şablonları Visual Studio.

MainWindow.xaml dosyası

WinUI ile XAML işaretlemesinde Window sınıfının örneklerini oluşturabilirsiniz.

XAML Penceresi sınıfı, masaüstü pencerelerini destekleyecek şekilde genişletilerek UWP ve masaüstü uygulaması modelleri tarafından kullanılan alt düzey pencere uygulamalarının her birinin özetine dönüştürülmüştür. Özellikle, UWP için CoreWindow ve Win32 için pencere tutamaçları (veya HWND'ler).

Aşağıdaki kod, uygulamanın kök öğesi olarak Window sınıfını kullanan ilk şablon uygulamasındaki MainWindow.xaml dosyasını gösterir.

<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>

Configuration

  1. User32.dll'dan dışarı aktarılan Win32 API'lerini çağırmak için Visual Studio project C#/Win32 P/Invoke Source Generator kullanabilirsiniz. Tools>NuGet Package Manager> Çözüm için NuGet Paketlerini Yönet... öğesine tıklayın. ve (Browse sekmesinde) Microsoft.Windows.CsWin32 araması yapın. Diğer ayrıntılar için bkz. Yönetilen Koddan Yerel İşlevleri Çağırma.

    İsteğe bağlı olarak Microsoft.Windows.CsWin32Dependencies>Packages Solution Explorer düğümü altında listelendiğini onaylayarak yüklemenin başarılı olduğunu doğrulayabilirsiniz.

    Ayrıca, isteğe bağlı olarak uygulama project dosyasına çift tıklayabilir (veya sağ tıklayıp project Dosyayı düzenle) dosyayı bir metin düzenleyicisinde açmak ve project dosyasının artık "Microsoft.Windows.CsWin32" için bir NuGet PackageReference içerdiğini onaylayabilirsiniz.

    <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>
    
  2. Projenize bir metin dosyası ekleyin ve adını NativeMethods.txt verin. Bu dosyanın içeriği C#/Win32 P/Invoke Kaynak Oluşturucu'ya P/Invoke kaynak kodunun oluşturulmasını istediğiniz işlevleri ve türleri bildirir. Başka bir deyişle, C# kodunuzda hangi işlevleri ve türleri çağırıp kullanacaksınız?

    GetDpiForWindow
    GetWindowLong
    SetWindowPos
    SetWindowLong
    HWND_TOP
    WINDOW_STYLE
    

Code

  1. App.xaml.cs Arka plan kod dosyasında, WindowNative.GetWindowHandle WinRT COM birlikte çalışma yöntemini kullanarak Window'a bir tanıtıcı elde ederiz (bkz. Bir pencere tutamacını alma (HWND)).

    Bu yöntem, burada gösterildiği gibi uygulamanın OnLaunched işleyicisinden çağrılır :

    /// <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();
    }
    
  2. Ardından Window tutamacını ve tercih edilen boyutları geçirerek bir SetWindowDetails yöntemi çağırırız.

    Bu yöntemde:

    • Pencere için inç başına nokta (dpi) değerini almak için GetDpiForWindow'u çağırırız (Win32 fiziksel pikselleri, WinUI ise etkin pikselleri kullanır). Bu dpi değeri ölçek faktörünü hesaplamak ve pencere için belirtilen genişlik ve yüksekliğe uygulamak için kullanılır.
    • Ardından, pencerenin istenen konumunu belirtmek için SetWindowPos'u çağırırız.
    • Son olarak, SetWindowLong çağrısı yaparak Küçült ve Büyüt düğmelerini devre dışı bırakırız.
    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);
    }
    
  3. MainWindow.xaml dosyasında, geçerli işlem için yüklenen tüm modüllerin listesini görüntülemek için ScrollViewer içeren bir ContentDialog kullanırız.

    <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>
    
  4. Ardından olay işleyicisini MyButton_Click aşağıdaki kodla değiştiririz.

    Burada GetCurrentProcess çağrısı yapılarak geçerli işleme bir referans elde edilir. Ardından Modüller koleksiyonu üzerinde yineleriz ve her bir ProcessModule dosya adını görüntü dizimize ekleriz.

    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();
    }
    
  5. Uygulamayı derleyin ve çalıştırın.

  6. Pencere göründükten sonra "Yüklenen modülleri görüntüle" düğmesini seçin.

    Bu konuda açıklanan temel Win32 birlikte çalışma uygulamasının ekran görüntüsü.
    Bu konuda açıklanan temel Win32 birlikte çalışma uygulaması.

Summary

Bu konu başlığında, temel pencere uygulamasına (bu örnekte Win32 ve HWND'ler) erişmeyi ve WinRT API'leriyle birlikte Win32 API'lerini kullanmayı ele aldık. Bu, yeni WinUI masaüstü uygulamaları oluştururken mevcut masaüstü uygulama kodunu nasıl kullanabileceğinizi gösterir.

Daha kapsamlı bir örnek için Windows App SDK Samples GitHub deposundaki AppWindow galeri örneğine bakın.

Pencere başlık çubuğunu özelleştirmek için bir örnek

Bu ikinci örnekte, pencerenin başlık çubuğunu ve içeriğini özelleştirmeyi göstereceğiz. Onunla birlikte takip etmeden önce şu konuları gözden geçirin:

Yeni project oluşturma

  1. Visual Studio'da, WinUI Boş Uygulaması (Paketlenmiş) proje şablonundan yeni bir C# veya C++/WinRT projesi oluşturun.

Configuration

  1. Yine ilk örnekte yaptığımız gibi Microsoft.Windows.CsWin32 NuGet paketine başvurun.

  2. Projenize bir NativeMethods.txt metin dosyası ekleyin.

    LoadImage
    SendMessage
    SetWindowText
    WM_SETICON
    

MainWindow.xaml

Note

Bu kılavuzla kullanmak için bir simge dosyası gerekiyorsa computer.ico örnek uygulamasından dosyası indirebilirsiniz. Bu dosyayı Assets klasörünüze yerleştirin ve dosyayı içerik olarak projeye ekleyin. Ardından url'sini Assets/computer.icokullanarak dosyaya başvurabileceksiniz.

Aksi takdirde, zaten sahip olduğunuz bir simge dosyasını kullanmaktan çekinmeyin ve aşağıdaki kod listelerinde bu dosyaya yapılan iki başvuruyu değiştirin.

  1. Aşağıdaki kod listesinde, iki düğme eklediğimiz ve her biri için MainWindow.xaml işleyicileri belirttiğimizi göreceksiniz. İlk düğmenin tıklama işleyicisinde (basicButton_Click), başlık çubuğu simgesini ve metnini ayarlarız. İkincisinde (customButton_Click), başlık çubuğunu customTitleBarPanel adlı StackPanel içeriğiyle değiştirerek daha önemli özelleştirmeler gösteriyoruz.
<?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

  1. basicButton_Click işleyicisinin aşağıdaki kod listesinde (özel başlık çubuğunu gizli tutmak için) customTitleBarPanelStackPanel'i daraltıyor ve ExtendsContentIntoTitleBar özelliğini olarak falseayarlıyoruz.
  2. Ardından, ana pencerenin pencere tutamacını (HWND) almak için IWindowNative::get_WindowHandle (C# için, birlikte çalışma yardımcı yöntemi GetWindowHandle kullanarak) çağırırız.
  3. Ardından, LoadImage ve SendMessage işlevlerini çağırarak uygulama simgesini (PInvoke.User32 NuGet paketini kullanarak C# için) ayarlayacağız.
  4. Son olarak, başlık çubuğu dizesini güncelleştirmek için SetWindowText'i çağırırız.
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");
}
  1. customButton_Click işleyicisinde customTitleBarPanelStackPanel görünürlüğünü Visible olarak ayarladık.
  2. Ardından ExtendsContentIntoTitleBar özelliğini olarak trueayarlayıp SetTitleBar'ı çağırarak customTitleBarPanelStackPanel'i özel başlık çubuğumuz olarak görüntüleriz.
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

  1. Dosya App.xaml'da, bu açıklamadan <!-- Other app resources here --> hemen sonra, aşağıda gösterildiği üzere başlık çubuğuna bazı özel renkli fırçalar ekledik.
<?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>
  1. Kendi uygulamanızda bu adımları takip ediyorsanız, şimdi project oluşturabilir ve uygulamayı çalıştırabilirsiniz. Aşağıdakine benzer bir uygulama penceresi görürsünüz (özel uygulama simgesiyle):

    Özelleştirme içermeyen şablon uygulaması.
    Şablon uygulaması.

  • Temel özel başlık çubuğu aşağıdadır:

    Özel uygulama simgesine sahip şablon uygulaması.
    Özel uygulama simgesine sahip şablon uygulaması.

  • Tamamen özel başlık çubuğu aşağıdadır:

    Özel başlık çubuğuna sahip şablon uygulaması.
    Özel başlık çubuğuna sahip şablon uygulaması.

Ayrıca bakınız