<

WPF: Comparing methods for clearing "TextBox" content.

Published on
13,569 Points
6,669 Views
4 Endorsements
Last Modified:
Approved
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
Author:jonnidip
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free