1
\$\begingroup\$

I created a control that loads text, with richtextblock and richtextblockoverflow. The control is not very fast indeed what I would like to speed up is the LoadText method. It's possible to do it?

MainPage.xaml:

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

<Page.DataContext>
    <local:ColumnWidthClass x:Name="ViewModel"/>
</Page.DataContext>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid x:Name="Menù" HorizontalAlignment="Left" Width="290" Padding="0" Margin="0,21,0,0">
        <Grid Background="White">
            <Grid.RowDefinitions>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
                <RowDefinition Height="50"/>
            </Grid.RowDefinitions>
            <Button  Grid.Row="0" x:Name="btnLoadText" Click="btnLoadText_Click" Content="Text Display" HorizontalAlignment="Center" VerticalAlignment="Center" Width="270" Foreground="White" Height="32"/>
            <Grid Grid.Row="1">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Button Grid.Column="0" x:Name="btnDecFont" Content="Aa-" HorizontalAlignment="Stretch" Click="btnDecFont_Click" Margin="10" Foreground="White"/>
                <Button Grid.Column="1" x:Name="btnIncFont" Content="aA+" HorizontalAlignment="Stretch" Click="btnIncFont_Click" Margin="10" Foreground="White"/>
            </Grid>
            <Grid Grid.Row="2">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
                <Button Grid.Column="0" x:Name="btnBack" Content="Back" HorizontalAlignment="Stretch" Click="btnBack_Click" Margin="10" Foreground="White"/>
                <Button Grid.Column="1" x:Name="btnForward" Content="Forward" HorizontalAlignment="Stretch" Click="btnForward_Click" Margin="10" Foreground="White"/>
            </Grid>
        </Grid>
    </Grid>
    <Grid x:Name="BaseGrid" Margin="320,10,30,10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Black">
        <ScrollViewer x:Name="PageViewer" SizeChanged="PageViewer_SizeChanged" Background="White" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Hidden" VerticalScrollMode="Disabled" HorizontalScrollMode="Enabled">
            <StackPanel x:Name="StackViewer" Orientation="Horizontal"/>
        </ScrollViewer>
    </Grid>
</Grid>

MainPage.xaml.cs:

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();

        timerSize.Interval = TimeSpan.FromMilliseconds(50);
        timerSize.Tick += timerSize_tick;

        timerFoBA.Interval = TimeSpan.FromMilliseconds(400);
        timerFoBA.Tick += timerFoBA_tick;

        StackViewer.Children.Add(TextOneRich);
        TextOneRich.Width = ViewModel.ColumnWidthInt;
        TextOneRich.TextAlignment = TextAlignment.Justify;
        binding = new Binding() { Path = new PropertyPath("ColumnWidthInt"), Source = ViewModel, Mode = BindingMode.OneWay };
        BindingOperations.SetBinding(TextOneRich, WidthProperty, binding);

        ViewModel.ColumnWidthInt = 400;
    }

    RichTextBlock TextOneRich = new RichTextBlock() { Margin = new Thickness(20) };
    List<RichTextBlockOverflow> TextList = new List<RichTextBlockOverflow>();
    DispatcherTimer timerSize = new DispatcherTimer();
    DispatcherTimer timerFoBA = new DispatcherTimer();
    Binding binding = new Binding();
    int numCol;
    bool TextLoaded = false;

    private async void timerSize_tick(object sender, object e)
    {
        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                timerSize.Stop();
                List<int> ChildrenList = new List<int>();
                ChildrenList.Clear();
                ChildrenList.Add(StackViewer.Children.Count - 1);

                for (int a = 0; a <= ChildrenList[0]; a++)
                {
                    if (a == 0)
                    {
                        RichTextBlock TextOne = StackViewer.Children[a] as RichTextBlock;
                        if (!TextOne.HasOverflowContent && a < ChildrenList[0])
                        {
                            for (int b = a + 1; b <= ChildrenList[0]; b++)
                            {
                                StackViewer.Children.RemoveAt(a + 1);
                                TextList.RemoveAt(a);
                            }
                            return;
                        }
                        else if (TextOne.HasOverflowContent && a == ChildrenList[0])
                        {
                            bool ThereIsText = true;
                            while (ThereIsText)
                            {
                                await Task.Delay(20);
                                if (TextList.Count == 0)
                                {
                                    await Task.Delay(20);
                                    if (TextOne.HasOverflowContent)
                                    {
                                        TextList.Add(new RichTextBlockOverflow() { Width = ViewModel.ColumnWidthInt, Margin = new Thickness(20) });
                                        StackViewer.Children.Add(TextList[TextList.Count - 1]);
                                        BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                        TextOne.OverflowContentTarget = TextList[TextList.Count - 1];
                                    }
                                    else
                                    {
                                        ThereIsText = false;
                                        BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                    }
                                }
                                else
                                {
                                    await Task.Delay(20);
                                    if (TextList[TextList.Count - 1].HasOverflowContent)
                                    {
                                        TextList.Add(new RichTextBlockOverflow() { Width = ViewModel.ColumnWidthInt, Margin = new Thickness(20) });
                                        BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                        StackViewer.Children.Add(TextList[TextList.Count - 1]);
                                        TextList[TextList.Count - 2].OverflowContentTarget = TextList[TextList.Count - 1];
                                    }
                                    else
                                    {
                                        ThereIsText = false;
                                        BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        RichTextBlockOverflow TextOne = StackViewer.Children[a] as RichTextBlockOverflow;
                        if (!TextOne.HasOverflowContent && a < ChildrenList[0])
                        {


                            for (int b = a + 1; b <= ChildrenList[0]; b++)
                            {
                                StackViewer.Children.RemoveAt(a + 1);
                                TextList.RemoveAt(a);
                            }

                            await Task.Delay(300);
                            if (TextList.Count > 0)
                            {
                                FindCorrectColumn("ChangeMeasures");
                                if (numCol > correctColumnNumber)
                                {
                                    ScrollToElement(PageViewer, TextList[numCol], 20, false, false, null);
                                }
                                else
                                {
                                    ScrollToElement(PageViewer, TextOneRich, 20, false, false, null);
                                }
                            }

                            BaseGrid.Opacity = 100;
                            return;
                        }
                        else if (TextOne.HasOverflowContent && a == ChildrenList[0])
                        {
                            bool ThereIsText = true;
                            await Task.Delay(300);
                            if (TextList.Count > 0)
                            {
                                FindCorrectColumn("ChangeMeasures");
                                if (numCol > correctColumnNumber)
                                {
                                    ScrollToElement(PageViewer, TextList[numCol], 20, false, false, null);
                                }
                                else
                                {
                                    ScrollToElement(PageViewer, TextOneRich, 20, false, false, null);
                                }
                            }
                            BaseGrid.Opacity = 100;

                            while (ThereIsText)
                            {
                                await Task.Delay(20);
                                if (TextList[TextList.Count - 1].HasOverflowContent)
                                {
                                    TextList.Add(new RichTextBlockOverflow() { Width = ViewModel.ColumnWidthInt, Margin = new Thickness(20) });
                                    BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                    StackViewer.Children.Add(TextList[TextList.Count - 1]);
                                    TextList[TextList.Count - 2].OverflowContentTarget = TextList[TextList.Count - 1];
                                }
                                else
                                {
                                    ThereIsText = false;
                                    BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                                }
                            }
                        }
                    }
                }
            });
        });
        BaseGrid.Opacity = 100;
    }

    private async void btnLoadText_Click(object sender, RoutedEventArgs e)
    {

        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                TextLoaded = true;
                Paragraph paragraphText = new Paragraph();
                paragraphText.Inlines.Add(new Run {Text = "" }); //** The text in the link must be pasted in the "Run" of this paragraph.
                LoadText(paragraphText);
            });
        });
    }

    private async void LoadText(Paragraph paragraph)
    {
        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                bool first = true;
                bool TereIsText = true;
                TextOneRich.Blocks.Add(paragraph);
                while (TereIsText)
                {
                    if (first)
                    {
                        await Task.Delay(20);
                        if (TextOneRich.HasOverflowContent)
                        {
                            TextList.Add(new RichTextBlockOverflow() { Width = ViewModel.ColumnWidthInt, Margin = new Thickness(20) });
                            BindingOperations.SetBinding(TextList[0], WidthProperty, binding);
                            StackViewer.Children.Add(TextList[0]);
                            TextOneRich.OverflowContentTarget = TextList[0];
                            first = false;
                        }
                        else
                        {
                            TereIsText = false;
                        }
                    }
                    else
                    {
                        await Task.Delay(20);
                        if (TextList[TextList.Count - 1].HasOverflowContent)
                        {
                            TextList.Add(new RichTextBlockOverflow() { Width = ViewModel.ColumnWidthInt, Margin = new Thickness(20) });
                            BindingOperations.SetBinding(TextList[TextList.Count - 2], WidthProperty, binding);
                            StackViewer.Children.Add(TextList[TextList.Count - 1]);
                            TextList[TextList.Count - 2].OverflowContentTarget = TextList[TextList.Count - 1];
                        }
                        else
                        {
                            TereIsText = false;
                            BindingOperations.SetBinding(TextList[TextList.Count - 1], WidthProperty, binding);
                        }
                    }
                }
            });
        });
    }

    double correctColumnNumber;
    double correctWidth;

    private void PageViewer_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        if (TextLoaded)
        {
            BaseGrid.Opacity = 0;
        }

        correctColumnNumber = Math.Truncate(PageViewer.ActualWidth / Convert.ToInt32(500)) + 1;
        correctWidth = (PageViewer.ActualWidth / correctColumnNumber) - 40;
        if (correctColumnNumber != 0)
        {
            ViewModel.ColumnWidthInt = correctWidth;
        }

        numCol = Convert.ToInt32(correctColumnNumber) - 1;
        timerSize.Stop();
        timerSize.Start();
    }

    private async void btnDecFont_Click(object sender, RoutedEventArgs e)
    {
        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                TextOneRich.FontSize -= 1;
                timerSize.Stop();
                timerSize.Start();
            });
        });
    }

    private async void btnIncFont_Click(object sender, RoutedEventArgs e)
    {
        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                TextOneRich.FontSize += 1;
                timerSize.Stop();
                timerSize.Start();
            });
        });
    }

    private void btnBack_Click(object sender, RoutedEventArgs e)
    {
        timerFoBA.Start();
        btnBack.IsEnabled = false;

        FindCorrectColumn("Back");
        if (numCol == 10000000)
        {

        }
        else if (numCol != -1)
        {
            ScrollToElement(PageViewer, TextList[numCol], 20, false, true, null);
        }
        else
        {
            ScrollToElement(PageViewer, TextOneRich, 20, false, true, null);
        }
    }

    private void btnForward_Click(object sender, RoutedEventArgs e)
    {
        if (PageViewer.ScrollableWidth != PageViewer.HorizontalOffset)
        {
            timerFoBA.Start();
            btnForward.IsEnabled = false;

            FindCorrectColumn("Forward");
            try
            {
                if (numCol == -1)
                {
                    numCol = 0;
                }
                ScrollToElement(PageViewer, TextList[numCol], 20, false, true, null);
            }
            catch { }
        }
    }


    private void timerFoBA_tick(object sender, object e)
    {
        timerFoBA.Stop();
        btnBack.IsEnabled = true;
        btnForward.IsEnabled = true;
    }

    private void ScrollToElement(ScrollViewer scrollViewer, UIElement element, double margin, bool isVerticalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
    {
        var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
        var position = transform.TransformPoint(new Point(0, 0));

        if (isVerticalScrolling)
        {
            scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
        }
        else
        {
            scrollViewer.ChangeView(position.X - margin, null, zoomFactor, !smoothScrolling);
        }
    }

    private void FindCorrectColumn(string ForwardBack)
    {
        bool columnFind = false;
        bool currentColumn = false;
        int exactColumn = 0;
        int cont = 0;
        while (!columnFind)
        {
            try
            {
                Rect elementBounds = TextList[cont].TransformToVisual(PageViewer).TransformBounds(new Rect(0.0, 0.0, TextList[cont].ActualWidth, TextList[cont].ActualHeight));
                Rect containerBounds = new Rect(0.0, 0.0, PageViewer.ActualWidth, PageViewer.ActualHeight);
                currentColumn = (elementBounds.Left < containerBounds.Right && elementBounds.Right > containerBounds.Left);
                if (currentColumn)
                {
                    exactColumn = cont;
                    columnFind = true;
                }
                else
                {
                    cont += 1;
                }
            }
            catch
            {
                numCol = -1;
                columnFind = true;
                return;
            }
        }

        if (ForwardBack == "Forward")
        {
            exactColumn += 1;
            numCol = Convert.ToInt32(((Math.Truncate((exactColumn / correctColumnNumber))) * correctColumnNumber) + (correctColumnNumber - 1));
        }
        else if (ForwardBack == "ChangeMeasures")
        {
            numCol = exactColumn;
        }
        else
        {
            numCol = Convert.ToInt32((Math.Truncate((exactColumn / correctColumnNumber)) * correctColumnNumber) - 1);
        }
    }
}

ColumnWidthClass.cs:

class ColumnWidthClass : INotifyPropertyChanged
{
    private double columnwidthint = 350;
    public double ColumnWidthInt
    {
        get
        {
            return columnwidthint;
        }
        set
        {
            columnwidthint = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ColumnWidthInt)));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

What I would like to speed up is the "LoadText" method but.. I can not..! A way to do it?

The text to paste in the paragraph "Run" in the btnLoadText_Click is in this link: Text to load.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

This is a lot of code that I cannot run so I'll be guessing...


await Task.Delay(20);

If you want to speed up your code why do you have delays there? They are not documented. Is this some kind of a hack because otherwise LoadText works too fast?


try
{
    if (numCol == -1)
    {
        numCol = 0;
    }
    ScrollToElement(PageViewer, TextList[numCol], 20, false, true, null);
}
catch { }

The other guess is about your usage of try/catch block here. I think you might use here invalid indexes. Instead of catching them you should use a simple condition to check if they are out of range.


while (!columnFind)
{
    try
    {
        Rect elementBounds = 
            TextList[cont]
                .TransformToVisual(PageViewer)
                .TransformBounds(new Rect(0.0, 0.0, TextList[cont].ActualWidth, TextList[cont].ActualHeight));
        Rect containerBounds = new Rect(0.0, 0.0, PageViewer.ActualWidth, PageViewer.ActualHeight);
        currentColumn = (elementBounds.Left < containerBounds.Right && elementBounds.Right > containerBounds.Left);
        if (currentColumn)
        {
            exactColumn = cont;
            columnFind = true;
        }
        else
        {
            cont += 1;
        }
    }
    catch
    {
        numCol = -1;
        columnFind = true;
        return;
    }
}

Here we have a similar situation. You should eliminate them and make sure the index is valid instead of misusing exception catching. Try to avoid exceptions first. Catching them costs you performance if it occurs too often.

\$\endgroup\$
1
  • \$\begingroup\$ Hello t3chb0t, for the second and third point that you described, although it is not a perfect code, it works well for me; but the first point you pointed out is what I would like to speed up. if you don't insert the delay of 20 milliseconds, the HasOverflowContent property of the RichTextBlock will always be false and will therefore exit the first number of the loop without completing the display of all the text. Precisely for this reason I wanted to understand if it was possible to reduce that delay by 20 milliseconds or in some way even eliminate the delay. \$\endgroup\$
    – Tibrus
    Commented Dec 11, 2017 at 11:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.