AI-Powered Smart Paste in WPF Text Input Layout for Effortless Data Entry

Lucy Muturi - Oct 15 - - Dev Community

TL;DR: Transform Your WPF Apps with AI-Powered Smart Paste! This blog guides you through integrating the smart paste feature into the Syncfusion WPF Text Input Layout control for customer feedback forms. By harnessing AI, you can streamline data entry processes and automatically organize pasted information into the correct fields, greatly enhancing accuracy and efficiency.

Say goodbye to manual data entry in your forms! This blog demonstrates implementing the Smart Paste feature in customer feedback forms using the Syncfusion WPF Text Input Layout control. Learn how to effortlessly paste feedback information directly into the appropriate fields, saving time and reducing errors.

Smart Paste allows customer service representatives to easily paste data into a feedback form, where AI sorts text, ratings, and other details into the appropriate fields. This improves data collection speed and consistency, allowing for faster insights and responses based on customer feedback.

Let’s get started!

Integrating Semantic Kernel with the WPF app

First, open Visual Studio and create a new WPF app.

Before using the AI-powered Smart Paste feature, ensure you have access to Azure OpenAI and set up a deployment in the Azure portal. You can find the Microsoft.SemanticKernel NuGet package in the NuGet Gallery and install it in your project.

Once you get your key and endpoint, follow these steps:

Step 1: Set up Semantic Kernel

To configure Semantic Kernel, we will assume using the GPT-35 model deployed under the name GPT35Turbo. Let’s start by creating the SemanticKernelService, as shown in the following code example.

internal class SemanticKernelService
{
    const string endpoint = "https://{YOUR_END_POINT}.openai.azure.com";
    const string deploymentName = "GPT35Turbo";
    string key = "API key";

    IChatCompletionService chatCompletionService;
    Kernel kernel;

    internal SemanticKernelService()
    {
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Connect to the Semantic Kernel

Let’s set up the connection to Azure OpenAI chat completion service. Refer to the following code.

// At the time of required.
var builder = Kernel.CreateBuilder().AddAzureOpenAIChatCompletion(deploymentName, endpoint, key);
this.kernel = builder.Build();
this.chatCompletionService = this.kernel.GetRequiredService<IChatCompletionService>();

Enter fullscreen mode Exit fullscreen mode

This connection allows you to send prompts to the model and receive responses, which can be used for smart paste functionality.

Step 3: Create product feedback data form model

Now, create a product feedback form using the Syncfusion WPF Text Input Layout. Before proceeding, please refer to the getting started with the WPF Text Input Layout documentation.

Create a feedback form and define a model with the following fields: Name, Email, Product Name, Product Version, Rating, and Comments.

Refer to the following code example, where we’ve implemented the FeedbackForm class, which also supports property change notifications through the INotifyPropertyChanged interface. This will allow the UI to update when any property values change automatically.

public class FeedbackForm : INotifyPropertyChanged
{
    private string name = string.Empty;
    private string email = string.Empty;
    private string productName = string.Empty;
    private string productVersion = string.Empty;
    private string rating = string.Empty;
    private string comments = string.Empty;

    ///<summary>
    /// Occurs when a property value changes.
    ///</summary> 
    public event PropertyChangedEventHandler? PropertyChanged;

    ///<summary>
    /// Invokes the property changed event.
    ///</summary> 
    ///<param name="propertyName">The property name.</param>
    protected void OnPropertyChanged(string propertyName)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public string Name
    {
        get => this.name;
        set
        {
            if (this.name != value)
            {
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }
    }

    public string Email
    {
        get => this.email;
        set
        {
            if (this.email != value)
            {
                this.email = value;
                this.OnPropertyChanged("Email");
            }
        }
    }

    public string ProductName
    {
        get => this.productName;
        set
        {
            if (this.productName != value)
            {
                this.productName = value;
                this.OnPropertyChanged("ProductName");
            }
        }
    }

    public string ProductVersion
    {
        get => this.productVersion;
        set
        {
            if (this.productVersion != value)
            {
                this.productVersion = value;
                this.OnPropertyChanged("ProductVersion");
            }
        }
    }

    public string Rating
    {
        get => this.rating;
        set
        {
            if (this.rating != value)
            {
                this.rating = value;
                this.OnPropertyChanged("Rating");
            }
        }
    }

    public string Comments
    {
        get => this.comments;
        set
        {
            if (this.comments != value)
            {
                this.comments = value;
                this.OnPropertyChanged("Comments");
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Create editors for the product feedback form

Then, define the required fields for the feedback form, such as name, email, product name, product version, rating, and comments using TextInputLayout controls for each input field. The TextBox elements within the layout will bind to the FeedbackForm properties in the ViewModel to handle data input.

Refer to the following code example for defining the input fields.

FeedbackFormViewModel.cs

public class FeedbackFormViewModel
{
    /// <summary>
    /// Gets or sets the feedback form model.
    /// </summary>
    public FeedbackForm FeedbackForm { get; set; }

    /// <summary>
    /// Initializes a new instance of the class.
    /// </summary>
    public FeedbackFormViewModel()
    {
        this.FeedbackForm = new FeedbackForm();
    }
}
Enter fullscreen mode Exit fullscreen mode

Design the interactive product feedback form page

In this step, the feedback form page will be designed to include images, buttons, and labels for an engaging user experience. The form will collect feedback information such as Name, Email, Product Name, Product Version, Rating, and Comments using TextInputLayout controls for each input field. Refer to the following code example that outlines the layout and structure for adding the feedback form.

<Grid>
  <!-- Set the DataContext to the FeedbackFormViewModel -->
  <Grid.DataContext>
    <local:FeedbackFormViewModel x:Name="feedbackFormViewModel" />
  </Grid.DataContext>

  <!-- Feedback Form Background Image -->
  <Image Stretch="Fill" 
         Source="/AISmartPasteDemo;component/Assets/feedbackform.png" />

  <!-- Feedback Form Container -->
  <Border MaxHeight="550" 
          Width="460" 
          Background="{DynamicResource ContentBackgroundAlt1}" 
          BorderBrush="{DynamicResource BorderAlt}" 
          BorderThickness="1" 
          CornerRadius="10" 
          VerticalAlignment="Center" 
          HorizontalAlignment="Center">
    <Grid Margin="10">
      <!-- Define the rows for the form layout -->
      <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
        <RowDefinition Height="50" />
      </Grid.RowDefinitions>

      <!-- Title Label for the Form -->
      <Label Grid.ColumnSpan="4" 
             Grid.Row="0" 
             FontSize="16" 
             FontWeight="Bold" 
             Content="Feedback Form" 
             HorizontalContentAlignment="Center" />

      <!-- Scrollable section for input fields -->
      <ScrollViewer Grid.Row="1" 
                    VerticalScrollBarVisibility="Auto">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
          </Grid.RowDefinitions>

          <inputLayout:SfTextInputLayout x:Name="nameTextInputLayout" 
                                         Grid.Row="0" 
                                         Hint="Name" 
                                         Width="auto" 
                                         HelperText="Enter your name" 
                                         HintFloatMode="Float" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.Name}" />
          </inputLayout:SfTextInputLayout>

          <inputLayout:SfTextInputLayout x:Name="emailTextInputLayout" 
                                         Grid.Row="1" 
                                         Hint="Email" 
                                         HelperText="Enter your email" 
                                         Margin="0,8,0,0" 
                                         Width="auto" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.Email}" />
          </inputLayout:SfTextInputLayout>

          <inputLayout:SfTextInputLayout x:Name="productNameTextInputLayout" 
                                         Grid.Row="2" 
                                         Margin="0,8,0,0" 
                                         Hint="Product Name" 
                                         Width="auto" 
                                         HelperText="Example: Scheduler" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.ProductName}" />
          </inputLayout:SfTextInputLayout>

          <inputLayout:SfTextInputLayout x:Name="prodectVersionTextInputLayout" 
                                         Grid.Row="3" 
                                         Width="auto" 
                                         Margin="0,8,0,0" 
                                         HelperText="Example: 26.2.8" 
                                         Hint="Product Version" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.ProductVersion}" />
          </inputLayout:SfTextInputLayout>

          <inputLayout:SfTextInputLayout x:Name="ratingTextInputLayout" 
                                         Grid.Row="4" 
                                         Hint="Rating" 
                                         Margin="0,8,0,0" 
                                         HelperText="Rating should be between 1 and 5" 
                                         Width="auto" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.Rating}" />
          </inputLayout:SfTextInputLayout>

          <inputLayout:SfTextInputLayout x:Name="commentsTextInputLayout" 
                                         Grid.Row="5" 
                                         Margin="0,8,0,0" 
                                         Hint="Comments" 
                                         Width="auto" 
                                         ContainerType="Outlined">
            <TextBox Text="{Binding FeedbackForm.Comments}" 
                     TextWrapping="Wrap" 
                     AcceptsReturn="True" />
          </inputLayout:SfTextInputLayout>
        </Grid>
      </ScrollViewer>

      <!-- Buttons for Smart Paste and Submit -->
      <StackPanel Grid.Row="2" 
                  HorizontalAlignment="Center" 
                  Orientation="Horizontal">
        <Button x:Name="smartPasteButton" 
                Width="150" 
                Height="35" 
                HorizontalAlignment="Center" 
                Click="OnSmartPasteButtonClicked" 
                Content="Smart Paste" 
                Style="{DynamicResource WPFPrimaryButtonStyle}" />
        <Button x:Name="submitButton" 
                Width="150" 
                Height="35" 
                Click="OnSubmitButtonClicked" 
                Margin="50,0,0,0" 
                HorizontalAlignment="Right" 
                Content="Submit" 
                Style="{DynamicResource WPFPrimaryButtonStyle}" />
      </StackPanel>
    </Grid>
  </Border>
</Grid>
Enter fullscreen mode Exit fullscreen mode

Configure AI service to process smart paste

Now, implement the GetResponseFromGPT method to send a prompt to the OpenAI API and retrieve the completion result.

Refer to the following code example.

internal class SemanticKernelService
{
    const string endpoint = "https://{YOUR_END_POINT}.openai.azure.com";
    const string deploymentName = "GPT35Turbo";
    string key = "API key";

    IChatCompletionService chatCompletionService;
    Kernel kernel;

    internal SemanticKernelService()
    {
    }

    internal async Task<string> GetResponseFromGPT(string userPrompt)
    {
        var builder = Kernel.CreateBuilder().AddAzureOpenAIChatCompletion(deploymentName, endpoint, key);
        this.kernel = builder.Build();
        if (this.kernel != null)
        {
            var chatHistory = new ChatHistory();
            chatHistory.Clear();

            // Add the user's prompt as a user message to the conversation.
            chatHistory.AddSystemMessage("You are a predictive analytics assistant.");

            // Add the user's prompt as a user message to the conversation.
            chatHistory.AddUserMessage(userPrompt);

            // Get the chat completions from kernel.
            chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();
OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new OpenAIPromptExecutionSettings();
                openAIPromptExecutionSettings.ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions;
            try
            {
                var response = await chatCompletionService.GetChatMessageContentAsync(chatHistory, executionSettings: openAIPromptExecutionSettings, kernel: kernel);
                return response.ToString();
            }
            catch
            {
                // If an exception occurs (e.g., network issues, API errors), return an empty string.
                return "";
            }
        }

        return "";
    }
}
Enter fullscreen mode Exit fullscreen mode

Enabling smart paste functionality for easy data entry

The Smart Paste fills out forms automatically using data from the user’s clipboard when clicking the Smart Paste button. This improves data management by accurately organizing and populating information in the correct fields.

Here, we’ll add the prompts, an AI service that intelligently merges clipboard data into a form’s JSON fields. It checks that the clipboard text matches the form’s object field names. The AI returns the merged JSON, which is then processed and incorporated into the form.

string clipboardText;
SemanticKernelService semanticKernelService = new SemanticKernelService();

private async void OnSmartPasteButtonClicked(object sender, RoutedEventArgs e)
{
    if (Clipboard.ContainsText())
    {
        this.clipboardText = Clipboard.GetText();
    }

    if (string.IsNullOrEmpty(this.clipboardText))
    {
        MessageBox.Show("No text copied to clipboard. Please copy the text and try again", "Information", MessageBoxButton.OK);
        return;
    }

    string dataFormJsonData = JsonConvert.SerializeObject(this.feedbackFormViewModel.FeedbackForm);
    string prompt = $"Merge the copied data into the DataForm field content, ensuring that the copied text matches suitable field names. Here are the details:" +
    $"\n\nCopied data: {this.clipboardText}," +
    $"\nDataForm Field Name: {dataFormJsonData}," +
    $"\nProvide the resultant DataForm directly." +
    $"\n\nConditions to follow:" +
    $"\n1. Do not use the copied text directly as the field name; merge appropriately." +
    $"\n2. Ignore case sensitivity when comparing copied text and field names." +
    $"\n3. Final output must be Json format" +
    $"\n4. No need any explanation or comments in the output" +
    $"\n Please provide the valid JSON object without any additional formatting characters like backticks or newlines";
    string finalResponse = await this.semanticKernelService.GetResponseFromGPT(prompt);
    this.ProcessSmartPasteData(finalResponse);
}
Enter fullscreen mode Exit fullscreen mode

Updating the product feedback form with smart paste data

After receiving the merged JSON response from the AI model, this step updates the feedback form fields with extracted values. The response is deserialized into a dictionary, which is then used to populate the corresponding fields in the feedback form.

private void ProcessSmartPasteData(string response)
{
    // Deserialize the JSON string to a Dictionary
    var openAIJsonData = JsonConvert.DeserializeObject<dictionary<string, object>>(response);

    // Create lists to hold field names and values
    var fieldNames = new List<string>();
    var fieldValues = new List<string>();

    foreach (var data in openAIJsonData)
    {
        fieldNames.Add(data.Key);
        fieldValues.Add(data.Value?.ToString() ?? string.Empty);
    }

    this.feedbackFormViewModel.FeedbackForm.Name = fieldValues[0];
    this.feedbackFormViewModel.FeedbackForm.Email = fieldValues[1];
    this.feedbackFormViewModel.FeedbackForm.ProductName = fieldValues[2];
    this.feedbackFormViewModel.FeedbackForm.ProductVersion = fieldValues[3];
    this.feedbackFormViewModel.FeedbackForm.Rating = fieldValues[4];
    this.feedbackFormViewModel.FeedbackForm.Comments = fieldValues[5];
}
Enter fullscreen mode Exit fullscreen mode

Validating the feedback form data using the WPF Text Input Layout control

We’ve created the feedback form; the next step involves implementing validation to ensure that the input data meets specific criteria. Refer to the following code example to validate the fields in the feedback form.

readonly string mailPattern = @"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@" +
    @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\." +
    @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|" +
    @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$";

private bool ValidateAllFields()
{
    bool isValidForm = true;
    FeedbackForm feedbackForm = this.feedbackFormViewModel.FeedbackForm;

    this.nameTextInputLayout.HasError = false;
    this.emailTextInputLayout.HasError = false;
    this.productNameTextInputLayout.HasError = false;
    this.prodectVersionTextInputLayout.HasError = false;
    this.ratingTextInputLayout.HasError = false;
    this.commentsTextInputLayout.HasError = false;

    //// Name Validation.
    if (string.IsNullOrEmpty(feedbackForm.Name))
    {
        isValidForm = false;
        this.nameTextInputLayout.HasError = true;
        this.nameTextInputLayout.ErrorText = "Enter your name";
    }
    else if (feedbackForm.Name.Length > 20)
    {
        isValidForm = false;
        this.nameTextInputLayout.HasError = true;
        this.nameTextInputLayout.ErrorText = "Name cannot exceed 20 characters";
    }

    //// Email Validation.
    if (string.IsNullOrEmpty(feedbackForm.Email))
    {
        isValidForm = false;
        this.emailTextInputLayout.HasError = true;
        this.emailTextInputLayout.ErrorText = "Enter your email";
    }
    else if (!Regex.IsMatch(this.feedbackFormViewModel.FeedbackForm.Email, this.mailPattern))
    {
        isValidForm = false;
        this.emailTextInputLayout.HasError = true;
        this.emailTextInputLayout.ErrorText = "Please enter a valid email address.";
    }

    //// Product Name Validation.
    if (string.IsNullOrEmpty(feedbackForm.ProductName))
    {
        isValidForm = false;
        this.productNameTextInputLayout.HasError = true;
        this.productNameTextInputLayout.ErrorText = "Enter product name";
    }
    else if (feedbackForm.Name.Length > 20)
    {
        isValidForm = false;
        this.productNameTextInputLayout.HasError = true;
        this.productNameTextInputLayout.ErrorText = "Product name cannot exceed 20 characters";
    }

    //// Product Version Validation.
    if (string.IsNullOrEmpty(feedbackForm.ProductVersion))
    {
        isValidForm = false;
        this.prodectVersionTextInputLayout.HasError = true;
        this.prodectVersionTextInputLayout.ErrorText = "Enter product version";
    }

    //// Rating Validation.
    if (string.IsNullOrEmpty(feedbackForm.Rating))
    {
        isValidForm = false;
        this.ratingTextInputLayout.HasError = true;
        this.ratingTextInputLayout.ErrorText = "Enter rating";
    }
    else if (!double.TryParse(feedbackForm.Rating, out double rating))
    {
        isValidForm = false;
        this.ratingTextInputLayout.HasError = true;
        this.ratingTextInputLayout.ErrorText = "Please enter a valid rating";
    }
    else if (rating < 1 || rating > 5)
    {
        isValidForm = false;
        this.ratingTextInputLayout.HasError = true;
        this.ratingTextInputLayout.ErrorText = "Rating should be between 1 and 5";
    }

    //// Comments Validation.
    if (string.IsNullOrEmpty(feedbackForm.Comments))
    {
        isValidForm = false;
        this.commentsTextInputLayout.HasError = true;
        this.commentsTextInputLayout.ErrorText = "Enter your comments";
    }

    return isValidForm;
}
Enter fullscreen mode Exit fullscreen mode

Show the validation message on the submit button click

To ensure that all required fields are filled out correctly before submission, we’ll validate the feedback form when the submit button is clicked using the ValidateAllFields method. This will help provide immediate feedback to the user regarding the form’s completeness.

Refer to the following code example.

private void OnSubmitButtonClicked(object sender, RoutedEventArgs e)
{
    if (this.ValidateAllFields())
    {
        MessageBox.Show("Feedback form submitted successfully", "Success", MessageBoxButton.OK);
    }
    else
    {
        MessageBox.Show("Please enter the required details", "Error", MessageBoxButton.OK);
    }
}
Enter fullscreen mode Exit fullscreen mode

After executing the previous code example, we will get the output as shown in the following image.

Integrating the AI-powered smart paste feature in the WPF Text Input Layout

Integrating the AI-powered smart paste feature in the WPF Text Input Layout

GitHub reference

For more details, refer to the AI-powered smart paste feature in the WPF Text Input Layout GitHub demo.

Conclusion

Thanks for reading! In this blog, we’ve seen how to create and validate a feedback form by implementing AI-powered smart paste functionality in the Syncfusion WPF Text Input Layout control. Try out this user-friendly control and share your feedback in the comments section below.

Existing customers can download the latest version of Essential Studio from the License and Downloads page. If you are not yet a customer, you can try our 30-day free trial to check out these new features.

If you have questions, you can contact us through our support forum, feedback portal, or support portal. We are always happy to assist you!

Related blogs

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .