WPF: Comparing methods for clearing "TextBox" content.

jonnidipDeveloper
Published:
What is better between TextBox.Text = String.Empty and TextBox.Clear()?

From an aesthetic view, I would choose the TextBox.Clear() method, because it is logically better to clear a value, rather than re-assigning it with another value. But I wanted to do a little test...

I am working with WPF and c#, in a .NET 4 environment:
Stopwatch sw = new Stopwatch();
                      sw.Start();
                      for (Int32 i = 0; i < 1000000; i++)
                          textBox_UserName.Text = String.Empty;
                      sw.Stop();
                      Int64 elapsed_StringEmpty = sw.ElapsedMilliseconds;
                      
                      sw.Reset();
                      sw.Start();
                      for (Int32 i = 0; i < 1000000; i++)
                          textBox_UserName.Clear();
                      sw.Stop();
                      Int64 elapsed_Clear = sw.ElapsedMilliseconds;

Open in new window

The two tests produced very different results:
1) Assign the value String.Empty to the Text property: 240 ms
2) Call the Clear() method: 10198 ms
This led me to avoid using the Clear() method which is 42 times (!!!) slower than assigning String.Empty value to the Text property.

The problem can be in part mitigated by surrounding the “Clear()” operation by a “BeginChange” / “EndChange”:
sw.Reset();
                      sw.Start();
                       textBox_UserName.BeginChange();
                       for (Int32 i = 0; i < 1000000; i++)
                          textBox_UserName.Clear();
                       textBox_UserName.EndChange();
                       sw.Stop();
                       Int64 elapsed_Clear_Change = sw.ElapsedMilliseconds;

Open in new window

In fact the same operation took now 6030 ms. It is still far from than the String.Empty assignment, but just a bit more than half the time of the previous execution.

The "Clear()" method implementation of the System.Windows.Controls.TextBox resides in the PresentationFramework.dll. If we take a look at it, then we can understand why it takes longer than the simple ".Text" assignment:
/// <summary> 
                      /// Clear all the content in the TextBox control.
                       /// </summary> 
                       public void Clear()
                       {
                          using (this.TextSelectionInternal.DeclareChangeBlock())
                           { 
                              this.TextContainer.DeleteContentInternal((TextPointer)this.TextContainer.Start, (TextPointer)this.TextContainer.End);
                               TextSelectionInternal.Select(this.TextContainer.Start, this.TextContainer.Start); 
                          } 
                      }

Open in new window

This code declares a "ChangeBlock" and then deletes the content. A ChangeBlock is defined as:
private class ChangeBlock : IDisposable 
                      {
                          internal ChangeBlock(ITextRange range, bool disableScroll) 
                          {
                              _range = range;
                              _disableScroll = disableScroll;
                              _range.BeginChange(); 
                          }
                       
                          void IDisposable.Dispose() 
                          {
                              _range.EndChange(_disableScroll, false /* skipEvents */); 
                              GC.SuppressFinalize(this);
                          }
                      
                          private readonly ITextRange _range; 
                          private readonly bool _disableScroll;
                      }  

Open in new window


The "DeleteContentInternal" method is:
// DeleteContent worker.  Removes content from the tree.
                      internal void DeleteContentInternal(TextPointer startPosition, TextPointer endPosition) 
                      {
                          TextTreeNode containingNode;
                          int symbolCount;
                          int charCount; 
                          TextTreeUndoUnit undoUnit;
                          TextPointer deletePosition; 
                       
                          startPosition.SyncToTreeGeneration();
                          endPosition.SyncToTreeGeneration(); 
                      
                          if (startPosition.CompareTo(endPosition) == 0)
                              return;
                       
                          BeforeAddChange();
                       
                          undoUnit = TextTreeUndo.CreateDeleteContentUndoUnit(this, startPosition, endPosition); 
                      
                          containingNode = startPosition.GetScopingNode(); 
                      
                          // Invalidate any TextElementCollection that depends on the parent.
                          // Make sure we do that before raising any public events.
                          TextElementCollectionHelper.MarkDirty(containingNode.GetLogicalTreeNode()); 
                      
                          int nextIMEVisibleNodeCharDelta = 0; 
                          TextTreeTextElementNode nextIMEVisibleNode = GetNextIMEVisibleNode(startPosition, endPosition); 
                          if (nextIMEVisibleNode != null)
                          { 
                              // The node following the delete just became the first sibling.
                              // This might affect its ime char count.
                              nextIMEVisibleNodeCharDelta = -nextIMEVisibleNode.IMELeftEdgeCharCount;
                              nextIMEVisibleNode.IMECharCount += nextIMEVisibleNodeCharDelta; 
                          }
                       
                          // First cut: remove all top-level TextElements and their chilren. 
                          // We need to put each TextElement in its own tree, so that any outside
                          // references can still play with the TextElements safely. 
                          symbolCount = CutTopLevelLogicalNodes(containingNode, startPosition, endPosition, out charCount);
                      
                          // Cut what's left.
                          int remainingCharCount; 
                          symbolCount += DeleteContentFromSiblingTree(containingNode, startPosition, endPosition, nextIMEVisibleNodeCharDelta != 0, out remainingCharCount);
                          charCount += remainingCharCount; 
                       
                          Invariant.Assert(symbolCount > 0);
                       
                          if (undoUnit != null)
                          {
                              undoUnit.SetTreeHashCode();
                          } 
                      
                          // Public tree event. 
                          deletePosition = new TextPointer(startPosition, LogicalDirection.Forward); 
                          AddChange(deletePosition, symbolCount, charCount, PrecursorTextChangeType.ContentRemoved);
                       
                          if (nextIMEVisibleNodeCharDelta != 0)
                          {
                              RaiseEventForNewFirstIMEVisibleNode(nextIMEVisibleNode);
                          } 
                      }

Open in new window

This is the core of the method and lets us understand why it is taking so much time.
The method calls "CreateDeleteContentUndoUnit" and then removes all the TextBox' children.

THIS IS THE REAL DIFFERENCE between "Clear()" and ".Text = "...
The Clear() method not only clears the text value, but removes all the content of the TextBox, which is what we really expect from it.

But what about ClearValue and SetValue methods?
DependencyProperty dp = System.Windows.Controls.TextBox.TextProperty;
                      
                      sw.Reset();
                      sw.Start();
                      for (Int32 i = 0; i < 1000000; i++)
                          textBox_UserName.ClearValue(dp);
                      sw.Stop();
                      Int64 elapsed_ClearValue = sw.ElapsedMilliseconds;
                      
                      sw.Reset();
                      sw.Start();
                      for (Int32 i = 0; i < 1000000; i++)
                          textBox_UserName.SetValue(dp, null);
                      sw.Stop();
                      Int64 elapsed_SetValue = sw.ElapsedMilliseconds;

Open in new window

The ClearValue method took 448 ms to execute 1 million times, while the SetValue method took 393 ms: closer to, but still about 1.5 times slower than the simple textBox_UserName.Text = String.Empty assignment.

If a simple "text clearing" is needed, then I would simply set "String.Empty" to the .Text property of my textbox. Otherwise, if the TextBox contains other objects, calling "Clear()" method would be best.
4
7,438 Views

Comments (4)

Mark WillsTopic Advisor
CERTIFIED EXPERT
Distinguished Expert 2018

Commented:
Good Article, enjoyed reading it and looking at the differences...

Thanks for writing it - voted Yes.
jonnidipDeveloper

Author

Commented:
@mark_willis: I am happy to know that you liked it.
Thank you!
CERTIFIED EXPERT
Author of the Year 2011
Top Expert 2006

Commented:
Very nicely done.
You have a nice way of presenting technical information in an easily read format.

"Yes" vote above.
jonnidipDeveloper

Author

Commented:
Thank you for reading!

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.