如何创建可变数量的RichTextBoxOverflow元素

时间:2017-12-12 15:46:53

标签: c# loops uwp

我试图以各种方式离开循环,必须根据任意输入文本长度创建多个RichTextBlockOverflow控件,但没有成功。 HasOverflowContent属性不会同步或异步更新。

变量bool" ThereIsText"我无法理解何时以及如何使其停止循环是错误的。

要粘贴在段落中的文本的链接"运行"是:text to paste

MainPage.xaml中:

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

<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"/>
            </Grid.RowDefinitions>
            <Button  Grid.Row="0" x:Name="btnLoadText" Click="btnLoadText_Click" Content="Display text" HorizontalAlignment="Center" VerticalAlignment="Center" Width="270" Foreground="White" Height="32"/>
            <TextBlock Grid.Row="1" x:Name="txtPage" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Grid>
    </Grid>
    <Grid x:Name="BaseGrid" Margin="320,10,30,10" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Black">
        <ScrollViewer x:Name="PageViewer" Background="White" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Visible" VerticalScrollMode="Disabled" HorizontalScrollMode="Enabled">
            <StackPanel x:Name="StackViewer" VirtualizingStackPanel.VirtualizationMode="Recycling" Orientation="Horizontal"/>
        </ScrollViewer>
    </Grid>
</Grid>

MainPage.xaml.cs中:

public sealed partial class MainPage : Page
{
    RichTextBlock TextOneRich = new RichTextBlock() { Margin = new Thickness(20) };
    List<RichTextBlockOverflow> TextList = new List<RichTextBlockOverflow>();
    bool ThereIsText = true;

    public MainPage()
    {
        this.InitializeComponent();

        StackViewer.Children.Add(TextOneRich);
        TextOneRich.Width = 400;
        TextOneRich.TextAlignment = TextAlignment.Justify;

    }

    private async void btnLoadText_Click(object sender, RoutedEventArgs e)
    {
        TextList.Clear();
        TextOneRich.Blocks.Clear();
        StackViewer.Children.Clear();
        StackViewer.Children.Add(TextOneRich);


        Paragraph paragraphText = new Paragraph();
        paragraphText.Inlines.Clear();
        paragraphText.Inlines.Add(new Run { Text = "PasteTextHere" });


        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                TextOneRich.Blocks.Add(paragraphText);
            });
        }).ContinueWith(async t =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                TextList.Add(new RichTextBlockOverflow() { Width = 400, Margin = new Thickness(20) });
                StackViewer.Children.Add(TextList[0]);
                TextOneRich.OverflowContentTarget = TextList[0];
            });
        });

        await Task.Run(async () =>
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                while (ThereIsText)
                {
                    await Task.Run(async () =>
                    {
                        await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                        {
                            TextList.Add(new RichTextBlockOverflow() { Width = 400, Margin = new Thickness(20) });
                            StackViewer.Children.Add(TextList[TextList.Count - 1]);
                            TextList[TextList.Count - 2].OverflowContentTarget = TextList[TextList.Count - 1];
                            txtPage.Text = TextList.Count.ToString();
                        });
                    });
                }
            });
        });
    }
}

1 个答案:

答案 0 :(得分:1)

如果您需要对UI对象进行大量操作,并且希望在执行此操作时保持UI响应(1),那么通常只需await一毫秒,这将允许UI处理任何输入消息等。

尝试访问HasOverflowContent属性是有问题的,因为它需要布局传递才能完成,这可能需要花费任意时间。我们可以await任意数量的时间 - 比如50毫秒 - 但这不是理想的。相反,您可以使用与"Waiting for XAML layout to complete"类似的技术稍作调整。

此XAML和代码将1000行文本添加到一组RichTextBlock / RichTextBlockOverflow控件中,同时保持UI响应(球保持移动,您可以在任何位置滚动列表)时间):

XAML:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <StackPanel Margin="20" HorizontalAlignment="Stretch" x:Name="container">
    <Ellipse Width="20" Height="20" Margin="0, 5" Fill="red"
             x:Name="animation" HorizontalAlignment="Left"/>
    <Button Content="Go" Click="Go" Margin="0,0,0,5"/>
    <ScrollViewer MaxHeight="500">
      <StackPanel x:Name="thePanel"/>
    </ScrollViewer>
  </StackPanel>
</Grid>

代码:

public static class Extensions
{
  // This helper function is essentially the same as this answer:
  // https://stackoverflow.com/a/14132711/4184842
  //
  // It adds an additional forced 1ms delay to let the UI thread
  // catch up.
  public static Task FinishLayoutAsync(this FrameworkElement element)
  {
    TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();

    // Setup handler that will be raised when layout has completed.
    EventHandler<object> handler = null;
    handler = (s, a) =>
    {
      element.LayoutUpdated -= handler;
      tcs.SetResult(true);
    };
    element.LayoutUpdated += handler;

    // Await at least 1 ms (to force UI pump) and until the Task is completed
    // If you don't wait the 1ms then you can get a 'layout cycle detected' error
    // from the XAML runtime.
    return Task.WhenAll(new[] { Task.Delay(1), tcs.Task });
  }
}

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

    // Simple animation to show the UI is not frozen
    BadUiAnimation_DontDoThis();
  }

  // Very VERY bad way of doing animation, but it shows
  // that the UI is still responsive. Normally you should
  // use StoryBoards to do animation.
  void BadUiAnimation_DontDoThis()
  {
    DispatcherTimer dt = new DispatcherTimer();
    dt.Interval = TimeSpan.FromMilliseconds(33);
    int delta = 4;
    const int width = 20;
    dt.Tick += (s, a) =>
    {
      var leftOffset = animation.Margin.Left;
      if (leftOffset + delta < 0)
      {
        delta *= -1;
        leftOffset = 0;
      }
      else if (leftOffset + delta + width > container.ActualWidth)
      {
        delta *= -1;
        leftOffset = container.ActualWidth - width;
      }
      else
      {
        leftOffset += delta;
      }

      animation.Margin = new Thickness(leftOffset, 5, 0, 5);
    };
    dt.Start();
  }

  private async void Go(object sender, RoutedEventArgs e)
  {
    // Helper function
    void AppendSimpleString(string s)
    {
      RichTextBlock rtb = new RichTextBlock();
      rtb.Blocks.Add(CreateParagraphWithText(s));
      thePanel.Children.Add(rtb);
    }

    // Another helper function
    Paragraph CreateParagraphWithText(string s)
    {
      var p = new Paragraph();
      var r = new Run();
      r.Text = s;
      p.Inlines.Add(r);
      return p;
    }

    // Disable the button so you can't click it again until the 
    // insertion is over
    (sender as Button).IsEnabled = false;
    thePanel.Children.Clear();

    AppendSimpleString($"Begin...{Environment.NewLine}");

    // Generate some dummy strings to add to the page
    var strings = new StringBuilder();
    for (int i = 0; i < 1000; i++)
      strings.Append($"This is line {i + 1}{Environment.NewLine}");

    string text = strings.ToString();

    // Create initial block with far too much text in it
    var source = new RichTextBlock();
    source.MaxHeight = 100;
    source.Blocks.Add(CreateParagraphWithText(text));
    thePanel.Children.Add(source);

    // Create the first overflow and connect it to the original textblock
    var prev = new RichTextBlockOverflow
    {
      MaxHeight = 100,
      Margin = new Thickness(0, 10, 0, 0)
    };
    thePanel.Children.Add(prev);
    source.OverflowContentTarget = prev;

    // Wait for layout to complete so we can check the 
    // HasOverflowContent property
    await prev.FinishLayoutAsync();

    // Keep creating more overflows until there is no content left
    while (prev.HasOverflowContent)
    {
      var next = new RichTextBlockOverflow
      {
        MaxHeight = 100,
        Margin = new Thickness(0, 10, 0, 0)
      };
      thePanel.Children.Add(next);
      prev.OverflowContentTarget = next;

      // Wait for layout to complete, which will compute whether there
      // is additional overflow (or not)
      await prev.FinishLayoutAsync();

      prev = next;
    };

    AppendSimpleString($"Done!{Environment.NewLine}");

    // Enable interaction with the button again
    (sender as Button).IsEnabled = true;
  }
}

(1):请注意,在发生这种情况时,您可能想要做一些事情来限制与UI的交互,这可能需要您禁用某些控件或以其他方式确保用户不会弄乱您的应用&# 39; s州。该示例通过禁用然后重新启用按钮来执行此操作。