Reputation: 1861
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
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