Logo

dev-resources.site

for different kinds of informations.

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

Published at
10/15/2024
Categories
wpf
ui
whatsnew
Author
lucy_muturi
Categories
3 categories in total
wpf
open
ui
open
whatsnew
open
Author
11 person written this
lucy_muturi
open
AI-Powered Smart Paste in WPF Text Input Layout for Effortless Data Entry

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

wpf Article's
30 articles in total
Favicon
[WPF] Draw Tree Topology
Favicon
Whatโ€™s New in WPF Diagram: 2024 Volume 4
Favicon
Implementing Full Transparency No Frame and Acrylic Effects in Avalonia UI
Favicon
๐—š๐—ฒ๐˜ ๐——๐—ฒ๐˜ƒ๐—˜๐˜…๐—ฝ๐—ฟ๐—ฒ๐˜€๐˜€ .๐—ก๐—˜๐—ง ๐— ๐—”๐—จ๐—œ ๐—–๐—ผ๐—ป๐˜๐—ฟ๐—ผ๐—น๐˜€ ๐—ณ๐—ผ๐—ฟ ๐—™๐—ฟ๐—ฒ๐—ฒ ๐—•๐—ฒ๐—ณ๐—ผ๐—ฟ๐—ฒ ๐——๐—ฒ๐—ฐ๐—ฒ๐—บ๐—ฏ๐—ฒ๐—ฟ ๐Ÿฏ๐Ÿญ, ๐Ÿฎ๐Ÿฌ๐Ÿฎ๐Ÿฐ!
Favicon
Por onde anda o WPF?
Favicon
Streamlining .NET Development with Practical Aspects
Favicon
Creating a Dynamic WPF Chart Dashboard to Showcase 2024 Womenโ€™s T20 World Cup Statistics
Favicon
In-Depth Technical Analysis of XAML-Based Frameworks and Cross-Platform Project Architecture Design
Favicon
Semantic Searching using Embedding in WPF DataGrid
Favicon
Integrating AI-Powered Smart Location Search in WPF Maps
Favicon
Create a WPF Multi-Bar Chart to Visualize U.S. vs. China Superpower Status
Favicon
AI-Powered Smart Paste in WPF Text Input Layout for Effortless Data Entry
Favicon
Latest LightningChart .NET Release: v.12.1.1 is out now!
Favicon
Create a WPF FastLine Chart to Analyze Global Population Trends by Age Group
Favicon
Introducing AI-Powered Smart Components & Features in Syncfusion Desktop Platforms
Favicon
Chart of the Week: Clean and Preprocess E-Commerce Website Traffic Data Using an AI-Powered Smart WPF Chart
Favicon
Everything You Need to Know About WPF Gantt Control
Favicon
Chart of the Week: Creating a WPF Chart Dashboard to Analyze 2024 T20 World Cup Statistics
Favicon
Whatโ€™s New in WPF Gantt Chart: 2024 Volume 2
Favicon
Chart of the Week: Creating a WPF Doughnut Chart to Visualize the New European Parliamentโ€™s Composition in 2024
Favicon
Chart of the Week: Creating a WPF Pie Chart to Visualize the Percentage of Global Forest Area for Each Country
Favicon
Chart of the Week: Creating a WPF Range Bar Chart to Visualize the Hearing Range of Living Beings
Favicon
Chart of the Week: Creating a WPF Sunburst Chart to Visualize the Syncfusion Chart of the Week Blog Series
Favicon
Chart of the Week: Creating a WPF Stacked Area Chart to Visualize Wealth Distribution in the U.S.
Favicon
Elevating Automation: Mastering PowerShell and C# Integration with Dynamic Paths and Parameters
Favicon
Navigate PDF Annotations in a TreeView Using WPF PDF Viewer
Favicon
Chart of the week: Creating a WPF 3D Column Chart to Visualize the Panama Canalโ€™s Shipment Transit Data
Favicon
Improve Real-Time WPF Visualization of ECG Signals With SciChart
Favicon
Race Strategy Analysis using SciChart WPF
Favicon
Chart of the Week: Creating a WPF Chart Dashboard to Visualize the 2023 World Billionaires List

Featured ones: