Udostępnij przez


Dodaj DALL-E do swojej aplikacji klasycznej WinUI 3 / Windows App SDK

W tym przewodniku zintegrujemy możliwości generowania obrazów DALL-E z aplikacją desktopową WinUI 3/Windows App SDK.

Prerequisites

Instalowanie i inicjowanie zestawu OpenAI SDK

Upewnij się, że biblioteka platformy .NET openAI jest zainstalowana w projekcie, uruchamiając polecenie dotnet add package OpenAI w oknie terminalu programu Visual Studio. Zainicjuj zestaw SDK przy użyciu klucza interfejsu API OpenAI w następujący sposób:

//...
using OpenAI;
using OpenAI.Chat;

namespace ChatGPT_WinUI3
{
    public sealed partial class MainWindow : Window
    {
        private OpenAIClient openAiService;

        public MainWindow()
        {
            this.InitializeComponent();
           
            var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");

            openAiService = new(openAiKey);
        }
    }
}

Modyfikowanie interfejsu użytkownika aplikacji

Zmodyfikuj istniejący DateTemplate w MainWindow.xaml, aby uwzględniał element kontrolny wyświetlający obrazy w konwersacji Image.

<!-- ... existing XAML ... -->
<ItemsControl.ItemTemplate>
    <DataTemplate>
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
            <Image Source="{Binding ImageUrl}" Margin="5" Stretch="UniformToFill"/>
        </StackPanel>
    </DataTemplate>
</ItemsControl.ItemTemplate>
<!-- ... existing XAML ... -->

Należy pamiętać, że zakłada się, iż masz interfejs czatu z elementem TextBox i Button. Zobacz Jak dodać uzupełnianie czatu OpenAI do aplikacji stacjonarnej w WinUI 3 / Windows App SDK.

Implementacja generowania obrazów DALL-E

W pliku MainWindow.xaml.csdodaj następujące metody do obsługi generowania i wyświetlania obrazu:

// ... existing using statements ...

private async void SendButton_Click(object sender, RoutedEventArgs e)
{
    ResponseProgressBar.Visibility = Visibility.Visible;
    string userInput = InputTextBox.Text;
    if (!string.IsNullOrEmpty(userInput))
    {
        InputTextBox.Text = string.Empty;
        // Use the DALL-E 3 model for image generation.
        ImageClient imageClient = openAiService.GetImageClient("dall-e-3");

        ClientResult<GeneratedImage> imageResult = await imageClient.GenerateImageAsync(userInput,
            new ImageGenerationOptions
            {
                Size = GeneratedImageSize.W1024xH1024,
                ResponseFormat = GeneratedImageFormat.Uri,
                EndUserId = "TestUser"
            });

        if (imageResult.Value != null)
        {
            AddImageMessageToConversation(imageResult.Value.ImageUri);
        }
        else
        {
            AddMessageToConversation("GPT: Sorry, something bad happened.");
        }
    }
    ResponseProgressBar.Visibility = Visibility.Collapsed;
}

private void AddImageMessageToConversation(Uri imageUrl)
{
    var imageMessage = new MessageItem
    {
        ImageUrl = imageUrl.ToString()
    };
    ConversationList.Items.Add(imageMessage);

    // handle scrolling
    ConversationScrollViewer.UpdateLayout();
    ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
}

Metoda imageClient.GenerateImageAsync() jest odpowiedzialna za wywoływanie API DALL-E OpenAI. Aby uzyskać więcej przykładów użycia, zapoznaj się z przykładami .NET OpenAI na GitHub.

Tip

Spróbuj poprosić firmę Microsoft Copilot o sugestie dotyczące różnych sposobów korzystania z interfejsów API DALL-E i czatów w aplikacji.

Zwróć uwagę na obecność ImageUrl w MessageItem klasie . Jest to nowa właściwość:

public class MessageItem
{
    public string Text { get; set; }
    public SolidColorBrush Color { get; set; }
    public string ImageUrl { get; set; } // new
}

Uruchamianie i testowanie

Uruchom aplikację, wprowadź monit i kliknij przycisk "Wyślij". Powinna zostać wyświetlona zawartość podobna do następującej:

Zrzut ekranu przedstawiający aplikację demonstracyjną generowania obrazów WinUI.

Dostosowywanie interfejsu użytkownika samodzielnie

Spróbuj dodać kilka przycisków radiowych do interfejsu użytkownika, aby wybrać, czy dołączyć obraz do konwersacji. Następnie można zmodyfikować metodę SendButton_Click w celu warunkowego wywołania metody generowania obrazu na podstawie wyboru przycisku radiowego.

Recap

W tym przewodniku pokazano, jak wykonać następujące działania:

  1. Zaakceptuj prośby o obrazy od użytkowników w obrębie elementu <TextBox>.
  2. Generowanie obrazów przy użyciu interfejsu API DALL-E OpenAI.
  3. Wyświetl obraz w obiekcie <Image>.

Pełne pliki kodu

Poniżej przedstawiono pełne pliki kodu do interfejsu czatu z generowaniem obrazów przez DALL-E. Kod został zaktualizowany w celu używania przycisków radiowych do warunkowego wywoływania czatu lub generowania obrazów zgodnie z sugestią w powyższej sekcji Dostosowywanie interfejsu użytkownika .

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="ChatGPT_WinUI3.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:ChatGPT_WinUI3"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <Grid VerticalAlignment="Bottom" HorizontalAlignment="Center">
        <StackPanel Orientation="Vertical" HorizontalAlignment="Center">
            <ScrollViewer x:Name="ConversationScrollViewer" VerticalScrollBarVisibility="Auto" MaxHeight="500">
                <ItemsControl x:Name="ConversationList" Width="300">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Vertical">
                                <TextBlock Text="{Binding Text}" TextWrapping="Wrap" Margin="5" Foreground="{Binding Color}"/>
                                <Image Source="{Binding ImageUrl}" Margin="5" Stretch="UniformToFill"/>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </ScrollViewer>
            <ProgressBar x:Name="ResponseProgressBar" Height="5" IsIndeterminate="True" Visibility="Collapsed"/>
            <StackPanel Orientation="Vertical" Width="300">
                <RadioButtons Header="Query type:">
                    <RadioButton x:Name="chatRadioButton" Content="Chat" IsChecked="True"/>
                    <RadioButton x:Name="imageRadioButton" Content="Image"/>
                </RadioButtons>
                <TextBox x:Name="InputTextBox" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" KeyDown="InputTextBox_KeyDown" TextWrapping="Wrap" MinHeight="100" MaxWidth="300"/>
                <Button x:Name="SendButton" Content="Send" Click="SendButton_Click" HorizontalAlignment="Right"/>
            </StackPanel>
        </StackPanel>
    </Grid>
</Window>

using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using OpenAI;
using OpenAI.Chat;
using OpenAI.Images;

namespace ChatGPT_WinUI3
{
    public class MessageItem
    {
        public string Text { get; set; }
        public SolidColorBrush Color { get; set; }
        public string ImageUrl { get; set; }
    }

    public sealed partial class MainWindow : Window
    {
        private OpenAIService openAiService;

        public MainWindow()
        {
            this.InitializeComponent();

            var openAiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY");

            openAiService = new(openAiKey);
        }

        private async void SendButton_Click(object sender, RoutedEventArgs e)
        {
            ResponseProgressBar.Visibility = Visibility.Visible;
            string userInput = InputTextBox.Text;

            try
            {
                if (imageRadioButton.IsChecked == true)
                {
                    await ProcessImageRequestAsync(userInput);
                }
                else
                {
                    await ProcessChatRequestAsync(userInput);
                }
            }
            catch (Exception ex)
            {
                AddMessageToConversation($"GPT: Sorry, something bad happened: {ex.Message}");
            }
            finally
            {
                ResponseProgressBar.Visibility = Visibility.Collapsed;
            }
        }

        private async Task ProcessImageRequestAsync(string userInput)
        {
            if (!string.IsNullOrEmpty(userInput))
            {
                InputTextBox.Text = string.Empty;
                // Use the DALL-E 3 model for image generation.
                ImageClient imageClient = openAiService.GetImageClient("dall-e-3");

                ClientResult<GeneratedImage> imageResult = await imageClient.GenerateImageAsync(userInput,
                    new ImageGenerationOptions
                    {
                        Size = GeneratedImageSize.W1024xH1024,
                        ResponseFormat = GeneratedImageFormat.Uri,
                        EndUserId = "TestUser"
                    });

                if (imageResult.Value != null)
                {
                    AddImageMessageToConversation(imageResult.Value.ImageUri);
                }
                else
                {
                    AddMessageToConversation("GPT: Sorry, something bad happened.");
                }
            }
        }

        private async Task ProcessChatRequestAsync(string userInput)
        {
            if (!string.IsNullOrEmpty(userInput))
            {
                AddMessageToConversation($"User: {userInput}");
                InputTextBox.Text = string.Empty;
                var chatClient = openAiService.GetChatClient("gpt-4o");
                var chatOptions = new ChatCompletionOptions
                {
                    MaxOutputTokenCount = 300
                };
                var completionResult = await chatClient.CompleteChatAsync(
                    [
                        ChatMessage.CreateSystemMessage("You are a helpful assistant."),
                        ChatMessage.CreateUserMessage(userInput)
                    ],
                    chatOptions);

                if (completionResult != null && completionResult.Value.Content.Count > 0)
                {
                    AddMessageToConversation($"GPT: {completionResult.Value.Content.First().Text}");
                }
                else
                {
                    AddMessageToConversation($"GPT: Sorry, something bad happened: {completionResult?.Value.Refusal ?? "Unknown error."}");
                }
            }
        }

        private void AddImageMessageToConversation(Uri imageUrl)
        {
            var imageMessage = new MessageItem
            {
                ImageUrl = imageUrl.ToString()
            };
            ConversationList.Items.Add(imageMessage);

            // handle scrolling
            ConversationScrollViewer.UpdateLayout();
            ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
        }

        private void AddMessageToConversation(string message)
        {
            var messageItem = new MessageItem
            {
                Text = message,
                Color = message.StartsWith("User:") ? new SolidColorBrush(Colors.LightBlue)
                                                    : new SolidColorBrush(Colors.LightGreen)
            };
            ConversationList.Items.Add(messageItem);

            // handle scrolling
            ConversationScrollViewer.UpdateLayout();
            ConversationScrollViewer.ChangeView(null, ConversationScrollViewer.ScrollableHeight, null);
        }

        private void InputTextBox_KeyDown(object sender, KeyRoutedEventArgs e)
        {
            if (e.Key == Windows.System.VirtualKey.Enter && !string.IsNullOrWhiteSpace(InputTextBox.Text))
            {
                SendButton_Click(this, new RoutedEventArgs());
            }
        }
    }
}