whd
whd

Reputation: 1861

Getting and setting large amount of text data in clipboard

I'm making simple tool app in C#, I have a textbox and I want to paste some text (around 300k lines), but this makes the app unresponsive. I waited like 10 minutes and nothing moved forward.

Is there some way to handle paste and copy operations on large data sets in smoother way? For example pasting and copying same amount of data in Windows Notepad takes just few seconds.

I use

Windows.ApplicationModel.DataTransfer.Clipboard.GetContent()

and at this app hangs. Example Code Xaml

<Window
    x:Class="App2.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App2"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1" Grid.RowSpan="1">
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ScrollViewer  Grid.Row="0"  Grid.RowSpan="1" Margin="5" VerticalScrollBarVisibility="Visible" >
            <TextBox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" IsReadOnly="False" Header="Query Result" Text='{x:Bind pasteResult, Mode=TwoWay}' PlaceholderText="Paste results here" TextWrapping="Wrap"/>
        </ScrollViewer>
    </Grid>
</Window>

cs file

using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.

namespace App2
{
    /// <summary>
    /// An empty window that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainWindow : Window
    {
        public string pasteResult;
        public MainWindow()
        {
            this.InitializeComponent();
        }

    }
}

Upvotes: 0

Views: 283

Answers (1)

Andrew KeepCoding
Andrew KeepCoding

Reputation: 13356

As @Simon Mourier mentioned in the comments, the performance problem is not related to the clipboard, but the TextBox control processing that amount of data.

So, let me give you another option using the ItemsRepeater which comes with virtualization built-in. (In my laptop) it takes approx. 3 secs to show 500K lines of text from the clipboard.

MainWindow.xaml

<Window
    x:Class="ClipboardTests.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid RowDefinitions="Auto,*">
        <StackPanel
            Grid.Row="0"
            Orientation="Horizontal">
            <Button
                Click="PasteButton_Click"
                Content="Paste" />
            <Button
                Click="ClearButton_Click"
                Content="Clear" />
            <TextBlock
                x:Name="MessageTextBox"
                VerticalAlignment="Center" />
        </StackPanel>

        <ScrollViewer Grid.Row="1">
            <ItemsRepeater x:Name="TextItemsRepeaterControl" />
        </ScrollViewer>
    </Grid>

</Window>

MainWindow.xaml.cs

using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel.DataTransfer;

namespace ClipboardTests;

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private static async Task<IEnumerable<string>> GetTextLinesFromClipboard()
    {
        DataPackageView dataPackageView = Clipboard.GetContent();

        if (dataPackageView.Contains(StandardDataFormats.Text) is true)
        {
            string text = await dataPackageView.GetTextAsync();

            string[] lines = text
                .ReplaceLineEndings()
                .Split(Environment.NewLine, StringSplitOptions.None);

            return lines;
        }

        return Enumerable.Empty<string>();
    }

    private async void PasteButton_Click(object sender, RoutedEventArgs e)
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
        IEnumerable<string> lines = await GetTextLinesFromClipboard();
        this.TextItemsRepeaterControl.ItemsSource = lines;
        stopwatch.Stop();
        this.MessageTextBox.Text = $"Pasted {this.TextItemsRepeaterControl.ItemsSourceView.Count} items in {stopwatch.Elapsed.TotalSeconds} s.";
    }

    private void ClearButton_Click(object sender, RoutedEventArgs e)
    {
        this.TextItemsRepeaterControl.ItemsSource = null;
    }
}

Upvotes: 1

Related Questions