Question: From the above WPF RichTextBox
, how can we programmatically get the list shown above?
Details: Using following Extension Methods, I can read all paragraphs from the above RichTextBox
. The btnTest_Click(...)
event (code shown below) returns all the paragraphs as follows (as you may have guessed from the above image), each line is a paragraph. But below code does not tell me which object (element) is a list:
Output of btnTest_Click(...) event:
This is a test.
Following is a list number:
1. Item 1
2. Item 2
3. Item 3
End of test.
MainWindow.xaml:
.....
<RichTextBox x:Name="rtbTest" AcceptsTab="True" FontFamily="Calibri"/>
.....
File1.cs:
using System.Windows.Documents;
using System.Linq;
namespace MyProjectName
{
public static class FlowDocumentExtensions
{
public static IEnumerable<Paragraph> Paragraphs(this FlowDocument doc)
{
return doc.Descendants().OfType<Paragraph>();
}
}
}
File2.cs:
using System.Windows;
using System.Linq;
namespace MyProjectName
{
public static class DependencyObjectExtensions
{
public static IEnumerable<DependencyObject> Descendants(this DependencyObject root)
{
if (root == null)
yield break;
yield return root;
foreach (var child in LogicalTreeHelper.GetChildren(root).OfType<DependencyObject>())
foreach (var descendent in child.Descendants())
yield return descendent;
}
}
}
Code to read all paragraphs:
private void btnTest_Click(object sender, RoutedEventArgs e)
{
foreach (Paragraph paragraph in rtbTest.Document.Paragraphs())
{
System.System.Diagnostics.Debug.WriteLine(new TextRange(paragraph.ContentStart, paragraph.ContentEnd).Text);
}
}
To enumerate all list items in a RichTextBox
select all System.Windows.Documents.List
from the RichTextBox.Document.Blocks
collection:
The MainWindow.xaml:
<Window ...
Title="MainWindow" Height="350" Width="500">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<RichTextBox Grid.Row="0" x:Name="rtb" AllowDrop="True" VerticalScrollBarVisibility="Auto" Padding="2">
<FlowDocument>
<Paragraph>
<Run Text="First list:"/>
</Paragraph>
<List MarkerStyle="Decimal">
<ListItem>
<Paragraph>C++</Paragraph>
</ListItem>
<ListItem>
<Paragraph>C#</Paragraph>
<List MarkerStyle="LowerLatin">
<ListItem>
<Paragraph>v 7.0</Paragraph>
</ListItem>
<ListItem>
<Paragraph>v 8.0</Paragraph>
</ListItem>
</List>
</ListItem>
</List>
<Paragraph>
<Run Text="Second list:"/>
</Paragraph>
<List MarkerStyle="Decimal">
<ListItem>
<Paragraph>Perl</Paragraph>
</ListItem>
<ListItem>
<Paragraph>Logo</Paragraph>
</ListItem>
</List>
</FlowDocument>
</RichTextBox>
<Button Grid.Row="1" Click="EnumerateList">Enumerate List</Button>
</Grid>
</Window>
The MainWindow.xaml.cs:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void EnumerateList(object sender, RoutedEventArgs e)
{
var lists = rtb.EnumerateLists();
for (int i=0; i < lists.Count; i++)
{
System.Diagnostics.Debug.WriteLine("\nList " + (i+1));
foreach (var litem in lists[i])
{
TextRange range = new TextRange(litem.ElementStart, litem.ElementEnd);
System.Diagnostics.Debug.WriteLine(range.Text);
}
}
}
}
public static class RichTextBoxExt
{
public static List<List<ListItem>> EnumerateLists(this RichTextBox rtb)
{
var result = new List<List<ListItem>>();
foreach (var block in rtb.Document.Blocks)
{
if (block is List list && list.ListItems.Count > 0)
{
//var marker = list.MarkerStyle;
result.Add(list.EnumerateList().ToList());
}
}
return result;
}
private static IEnumerable<ListItem> EnumerateList(this System.Windows.Documents.List list)
{
foreach (var litem in list.ListItems) yield return litem;
}
}
The code above produces the following output:
List 1
1. C++
2. C#
a. v 7.0
b. v 8.0
List 2
1. Perl
2. Logo
NOTE:
If some list item has subitems (
SiblingListItems
), in example above therange.Text
will contain"2.\tC#\r\na.\tv 7.0\r\nb.\tv 8.0"
. That means all sibling list items included to the text of parent item but separated by\r\n
.