Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Virtualized RichEditBox #2858

Closed
blakepell opened this issue Mar 13, 2019 · 9 comments
Closed

Feature Request: Virtualized RichEditBox #2858

blakepell opened this issue Mar 13, 2019 · 9 comments
Labels
controls 🎛️ feature request 📬 A request for new changes to improve functionality help wanted Issues identified as good community contribution opportunities

Comments

@blakepell
Copy link

blakepell commented Mar 13, 2019

I'm submitting a feature request for a Virtualized RichEditBox

Uservoice: https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/37090696-virtualized-richeditbox

I've had difficulty located examples on how to display and/or edit large files in UWP. It's my understanding the solution to this would be a VirtualizedRichEditBox. The current RichEditBox (or RichTextBlock, or TextBlock) are not virtualized and become incredibly slow with even moderate amounts of data. In my setup, greater than 4,000 lines and the UI because incredibly laggy. The only examples of this I can locate are vended products. I have worked around this issue by using a ListView but it is less than ideal because you lose the ability to do things such as select over lines that you would have in a traditional text box or text block.

@michael-hawker michael-hawker added help wanted Issues identified as good community contribution opportunities controls 🎛️ uservoice-entry-created feature request 📬 A request for new changes to improve functionality labels Mar 13, 2019
@dpaulino
Copy link
Contributor

dpaulino commented Mar 14, 2019

I want to climb atop a mountain and scream, "I need this too".

I built a UWP rest api client, and while it's mostly stable, the one thing that can reliably crash it is when the user pings an endpoint that returns over thousands of lines of json/xml. That breaks the textbox I use to display the response string and I'm livid that I cannot do anything about this since it's a platform limitation.

I have been searching for months on a virtualized textbox but no luck. I would love to see work on this happen.

Edit: I don't have much experience building a control from scratch, but if someone would like to try building this, I will contribute wherever I can.

@blakepell
Copy link
Author

blakepell commented Mar 14, 2019

@dpaulino I'm exploring using Win2d to create my own text block which I've never used so I don't know all the in's and out's with it. As a prototype I created a List with 1,000,000 elements, a CanvasControl and a ScrollBar. When the ScrollBar "Scroll" event fired I called "Invalidate" on the CanvasControl which fires it's "Draw" method. If you were creating TextBlocks with Runs you'd just replace those with DrawText calls in place of those and I think it would be straightforward.

In the Draw method I used a Linq query to get the next 50 records based off of the ScrollBar position. The result was I put a million lines onto the screen and it scrolled without a hiccup all the way up and down the list (it has the capability to show those lines, it only renders how many I tell it at a time). My CPU spiked at 6% when I was dragging the scroll bar up and down. When I was going line by line or with page up and page down it was about 2%-3%. I didn't do any parsing however, it was just straight text (I'm parsing ANSI colors from a TCP/IP stream so my use case is pretty simple).

I think I maybe able to do something with this.

My prototype doesn't do these things I would need to research and trial and error with:

1.) It doesn't have any concept of how many lines the screen is capable of displaying. I set the window size and only drew 50 at a time which I knew would fit. I used a set font size and tried to calculate the vertical position in a way that I'm not sure would translate well to other fonts or DPI's, I would need to learn more about how to handle that.
2.) It does not handle word wrapping.
3.) To dove tail on 2, I made an assumption that there are line breaks. I would have these in my case but in the real world tons of use cases wouldn't.
4.) Text is not selectable because it's drawn onto the screen. I did see a Microsoft demo app Win2d in the Windows Marketplace that had one control where text drawn was selectable so I'm going to that GitHub page later to see how they did that.

I think what I'll try to do is get something that works for me and then try to abstract that into something that's re-usable. I'll keep you posted if I make decent progress on it.

@dpaulino
Copy link
Contributor

@blakepell sounds good. If you're also willing to try, there's a powerful WPF text editor control called Avalon Edit. I wonder if we could fork it and make it run on UWP? https://github.com/icsharpcode/AvalonEdit

@dpaulino
Copy link
Contributor

Looks like a couple people tried to bring it to UWP three years ago, but they abandoned it almost as soon as they started. https://github.com/coderobin/AvalonEdit

@blakepell
Copy link
Author

I saw that also. :D Hehe. The Monaco editor was something I looked at also (that's the editor behind VS Code). There's a wrapper control to use it in UWP but I think it also has some performance issues with too much content.

@blakepell
Copy link
Author

blakepell commented Mar 14, 2019

@dpaulino

To show you what I was playing with and how little code it was initially. This was in a blank uwp app with Win2d added via NuGet. You can get a feel for how fast it scrolls and renders.

<Page xmlns:Controls="using:App1.MonocleGiraffe.Controls" 
    x:Class="App1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"      
    mc:Ignorable="d" Loaded="Page_Loaded"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="Auto"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        
        <canvas:CanvasControl Name="Terminal" Draw="Terminal_Draw" ClearColor="#1E1E1E" Grid.Column="0"  />
        <ScrollBar Name="TerminalScrollBar" Scroll="TerminalScrollBar_Scroll" Grid.Column="1" IndicatorMode="MouseIndicator" Orientation="Vertical" SmallChange="1" LargeChange="25"></ScrollBar>
    </Grid>          
</Page>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        CanvasTextFormat FontData = new CanvasTextFormat();

        public List<string> Lines = new List<string>();

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            FontData.FontFamily = "Courier New";
            FontData.FontSize = 12;

            for (int i = 1; i <= 1000000; i++)
            {
                Lines.Add($"Line {i}");
            }

            TerminalScrollBar.Minimum = 0;
            TerminalScrollBar.Maximum = Lines.Count - 1;
        }

        private void Terminal_Draw(CanvasControl sender, CanvasDrawEventArgs args)
        {
            var query = Lines
              .Skip((int)TerminalScrollBar.Value)
              .Take(50);

            int i = 0;

            foreach (string line in query)
            {
                i++;
                args.DrawingSession.DrawText(line, 0, (i * FontData.FontSize), Colors.LightGray, FontData);
            }

        }

        private void TerminalScrollBar_Scroll(object sender, ScrollEventArgs e)
        {
            Terminal.Invalidate();
        }

    }

@dpaulino
Copy link
Contributor

There's a uservoice request for this feature to be built by the platform team: https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/37090696-virtualized-richeditbox

@dpaulino
Copy link
Contributor

I've spent some time thinking of who would be best able to tackle the creation of this control from a first-party standpoint, and I think the owners of the WinUI library would be best (https://github.com/Microsoft/microsoft-ui-xaml). I may try to submit a feature proposal for this virtualized textbox/richeditbox control when I get some free time.

For reference, here is their feature proposal process: https://github.com/Microsoft/microsoft-ui-xaml/blob/master/docs/feature_proposal_process.md

@blakepell
Copy link
Author

With .NET Core 3 I've decided to migrate my project from UWP to WPF, less road blocks and red tape. @dpaulino I'm actually going to use the project you mentioned, Avalon Edit, which now has .NET Core 3 support (that editor is rock solid in everything I've thrown at it). In a very minimal amount of code I was able to get it render all of the ANSI sequences I need to support. I put half a million lines into it with millions of changing color sequences without so much as a hiccup on the performance front (scrolls from top to bottom and left to write like a champ).

Avalon Edit is so versatile and can be used for so many use cases I think something like it would be wonderful in this project.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
controls 🎛️ feature request 📬 A request for new changes to improve functionality help wanted Issues identified as good community contribution opportunities
Projects
None yet
Development

No branches or pull requests

4 participants