Reputation: 163
What is the best way to detect if a WPF RichTextBox/FlowDocument is empty?
The following works if only text is present in the document. Not if it contains UIElement's
new TextRange(Document.ContentStart, Document.ContentEnd).IsEmpty
Upvotes: 12
Views: 11623
Reputation: 5234
Here's an extension of H.B.'s idea that works with both text and images.
I found that difference is always >4 whenever the RTB has text. However, if you only paste an image it is 3. To combat this i look at the string length of the raw rtf string.
var start = Document.ContentStart;
var end = Document.ContentEnd;
var difference = start.GetOffsetToPosition(end);
HasText = difference > 4 || GetRtfText().Length > 350;
public string GetRtfText()
{
var tr = new TextRange(Document.ContentStart, Document.ContentEnd);
using (var ms = new MemoryStream())
{
tr.Save(ms, DataFormats.Rtf);
return Encoding.Default.GetString(ms.ToArray());
}
}
Through my testing i found that an empty box with no chars has a length of 270. If i even paste in an image that's only 1 pixel in size it balloons to 406.
I played with toggling on various formatting options without typing any letters and haven't gotten close to 300, so I went with 350 for the baseline.
The length check could be expensive if there are no textual characters, but they pasted in a massive image.
Upvotes: 0
Reputation: 3863
First - thank you to McGarnagle - their answer got me going in the right direction. However for whatever reason their image check didn't work for me. This is what I ended up doing:
Private Function RichTextBoxIsEmpty(BYVAL rtb As RichTextBox) As Boolean
Dim ReturnCode As Boolean = True
Dim text As String = New TextRange(rtb.Document.ContentStart, rtb.Document.ContentEnd).Text
If String.IsNullOrWhiteSpace(text) Then
For Each block As Block In rtb.Document.Blocks
'check for an image
If TypeOf block Is Paragraph Then
Dim paragraph As Paragraph = DirectCast(block, Paragraph)
For Each inline As Inline In paragraph.Inlines
If TypeOf inline Is InlineUIContainer Then
Dim uiContainer As InlineUIContainer = DirectCast(inline, InlineUIContainer)
If TypeOf uiContainer.Child Is Image Then
ReturnCode = False
Exit For
End If
End If
Next
End If
' Check for a table
If TypeOf block Is Table Then
ReturnCode = False
Exit For
End If
Next
Else
ReturnCode = False
End If
Return ReturnCode
End Function
there may be other checks to do, but this at least covers text, images and tables.
Upvotes: 0
Reputation: 102793
H.B.'s answer isn't useful if you need to distinguish between images and whitespace. You can use something like this answer to check for images.
bool IsEmpty(Document document)
{
string text = new TextRange(Document.ContentStart, Document.ContentEnd).Text;
if (string.IsNullOrWhiteSpace(text) == false)
return false;
else
{
if (document.Blocks.OfType<BlockUIContainer>()
.Select(c => c.Child).OfType<Image>()
.Any())
return false;
}
return true;
}
This seems laborious, and still probably isn't correct for all scenarios. But I couldn't find any better way.
Upvotes: 4
Reputation: 10274
The answer above works if you don't put anything into the RTB. However, if you simply delete the contents, the RTB tends to return a single, empty paragraph, not a completely empty string. So, this is more reliable in such cases:
string text = new TextRange(Document.ContentStart, Document.ContentEnd).Text;
return !String.IsNullOrWhiteSpace(text);
This only applies to textual contents, of course.
Upvotes: 2
Reputation: 185180
You could compare the pointers, which is not all too reliable:
var start = rtb.Document.ContentStart;
var end = rtb.Document.ContentEnd;
int difference = start.GetOffsetToPosition(end);
This evaluates to 2
if the RTB is loaded, and 4
if content has been entered and removed again.
If the RTB is completely cleared out e.g. via select all -> delete
the value will be 0
.
In the Silverlight reference on MSDN another method is found which can be adapted and improved to:
public bool IsRichTextBoxEmpty(RichTextBox rtb)
{
if (rtb.Document.Blocks.Count == 0) return true;
TextPointer startPointer = rtb.Document.ContentStart.GetNextInsertionPosition(LogicalDirection.Forward);
TextPointer endPointer = rtb.Document.ContentEnd.GetNextInsertionPosition(LogicalDirection.Backward);
return startPointer.CompareTo(endPointer) == 0;
}
Upvotes: 14