Reputation: 4577
I have a WPF textblock set up with the property TextWrapping="Wrap".
When I pass in a long string with a tab character (vbTab in my case) at the start, I would like the wrapping to honour this and keep the wrapped parts of the string indented. For example, instead of:
[vbTab]thisisreallylong
andwrapped
I want
[vbTab]thisisreallylong
[vbTab]andwrapped
and ideally for multiple tabs, etc. too.
[edit - additional details]
Because the textblock will be of variable size and contain multiple lines of text with various amounts of indenting, I can't just have a margin or manually split the strings and add tabs.
Essentially what I want is for it to treat lines of text like paragraphs, that keep their indenting when they wrap.
Upvotes: 1
Views: 1905
Reputation: 13679
Based on your idea, I am able to come up with this solution
I'll convert all the tabs in the beginning of every line to .5 inch margin each and will add the same text in a paragraph and apply the calculated margin to the same
A TextBlock was not feasible for the same as it is useful for basic text inlines like run bold, inline ui container etc. adding paragraph was more complicated in a TextBlock so I made the solution based on FlowDocument.
Result
below example demonstrate the same using FlowDocumentScrollViewer
or RichTextBox
or FlowDocumentReader
or plain FlowDocument
I have created the solution using attached properties, so you can attach the same to any of the mentioned or even add your own host for the document. you simply have to set IndentationProvider.Text
to the desired host.
XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:l="clr-namespace:PreservingIndentationDemo"
Title="MainWindow"
Height="350"
Width="525">
<Window.Resources>
<sys:String x:Key="longString"
xml:space="preserve">	this is really long and wrapped
		another line this is also really long and wrapped
	one more line this is also really long and wrapped
another line this is also really long and wrapped
		another line this is also really long and wrapped
</sys:String>
</Window.Resources>
<Grid>
<FlowDocumentScrollViewer l:IndentationProvider.Text="{StaticResource longString}" />
<!--<RichTextBox l:TextToParaHelper.Text="{StaticResource longString}" IsReadOnly="True"/>-->
<!--<FlowDocumentReader l:TextToParaHelper.Text="{StaticResource longString}" />-->
<!--<FlowDocument l:TextToParaHelper.Text="{StaticResource longString}" />-->
</Grid>
</Window>
	
refers to tab char
IndentationProvider
Class IndentationProvider
Public Shared Function GetText(obj As DependencyObject) As String
Return DirectCast(obj.GetValue(TextProperty), String)
End Function
Public Shared Sub SetText(obj As DependencyObject, value As String)
obj.SetValue(TextProperty, value)
End Sub
' Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
Public Shared ReadOnly TextProperty As DependencyProperty = DependencyProperty.RegisterAttached("Text", GetType(String), GetType(IndentationProvider), New PropertyMetadata(Nothing, AddressOf OnTextChanged))
Private Shared Sub OnTextChanged(d As DependencyObject, e As DependencyPropertyChangedEventArgs)
Dim blocks As BlockCollection = Nothing
Dim rtb As RichTextBox = TryCast(d, RichTextBox)
If rtb IsNot Nothing Then
rtb.Document.Blocks.Clear()
blocks = rtb.Document.Blocks
End If
If blocks Is Nothing Then
Dim fd As FlowDocument = TryCast(d, FlowDocument)
If fd IsNot Nothing Then
fd.Blocks.Clear()
blocks = fd.Blocks
End If
End If
If blocks Is Nothing Then
Dim fdr As FlowDocumentReader = TryCast(d, FlowDocumentReader)
If fdr IsNot Nothing Then
fdr.Document = New FlowDocument()
blocks = fdr.Document.Blocks
End If
End If
If blocks Is Nothing Then
Dim fdr As FlowDocumentScrollViewer = TryCast(d, FlowDocumentScrollViewer)
If fdr IsNot Nothing Then
fdr.Document = New FlowDocument()
blocks = fdr.Document.Blocks
End If
End If
Dim newValue As String = TryCast(e.NewValue, String)
If Not String.IsNullOrWhiteSpace(newValue) Then
For Each line As String In newValue.Split(ControlChars.Lf)
Dim leftMargin As Double = 0
Dim newLine As String = line
While newLine.Length > 0 AndAlso newLine(0) = ControlChars.Tab
leftMargin += 0.5
newLine = newLine.Remove(0, 1)
End While
Dim marginInch As String = leftMargin & "in"
Dim marginDip As Double = CDbl(New LengthConverter().ConvertFromString(marginInch))
Dim para As New Paragraph(New Run(newLine)) With {.Margin = New Thickness(marginDip, 0, 0, 0)}
blocks.Add(para)
Next
End If
End Sub
End Class
Demo
try demo project
Upvotes: 4