Click here to Skip to main content
15,885,216 members
Articles / Desktop Programming / WPF

WPF TextBox With Ellipsis

Rate me:
Please Sign up or sign in to vote.
5.00/5 (15 votes)
13 Jan 2012MIT3 min read 81K   2.9K   32   19
A subclass of the WPF TextBox control that displays an ellipsis when the text doesn't fit.

Introduction

The WPF TextBox class does not have a built-in option for automatically showing an ellipsis when the text is truncated to fit the visible area. Since I wanted this feature for a project I'm working on, I created my own subclass of TextBox called TextBoxWithEllipsis that does show an ellipsis when appropriate. I also included the ability to place the ellipsis to the left, right, or center of the visible text.

TextBoxWithEllipsis/TextBoxWithEllipsis.png

TextBoxWithEllipsis Properties

TextBoxWithEllipsis has the following properties in addition to those inherited from TextBox. I didn't bother to make any of them dependency properties, as I didn't need to for my purposes.

Property Description
LongText The underlying, untruncated text. When this doesn't fit in the visible area, the Text property is automatically set to a truncated version of this, but with an ellipsis. You can set Text or LongText interchangeably. However, when getting Text, you will get the (possibly) truncated version. Getting LongText always gets the untruncated version.
IsEllipsisEnabled A bool that enables and disables the ellipsis. When enabled, an ellipsis (Unicode character 0x2026) is shown when LongText is truncated to fit the visible area. Otherwise, the control behaves like a regular TextBox.
UseLongTextForToolTip When true, this bool causes the ToolTip property to automatically switch between LongText (when it doesn't fit) and null (when it does fit). This occurs independently of IsEllipsisEnabled. When false, ToolTip is left alone.
EllipsisPlacement An enumeration that specifies where the ellipsis should appear (<code>Left, Center, or Right).
FudgePix The number of pixels to "fudge" by when determining what substring of LongText will fit. I added this because I noticed the control wasn't quite using all the available space for the text before applying the ellipsis. The default value is 3, which appears to use every available pixel on my computer/monitor/video card. I made it adjustable in case it's not the same everywhere.

Demo App

The demo app included in the download is a Visual Studio 2010 C# WPF project containing two relevant files:

  • TextBoxWithEllipsis.cs - Implementation of the TextBoxWithEllipsis class. You can simply copy this file into your own project, though you'll probably want to change the namespace.
  • MainWindow.xaml - The resizable WPF window, shown above, that tests and demonstrates the TextBoxWithEllipsis control.

You can type directly into the "TextBox With Ellipsis" control or enter something in the "Source Text" field and click the button to test programmatically setting the Text property of TextBoxWithEllipsis. The checkboxes and radio buttons are hooked up to the appropriate properties of the TextBoxWithEllipsis control.

The control automatically adjusts the text it displays when it's resized. The ellipsis disappears if the control becomes long enough to accommodate all of the text (in the demo app, the control resizes with the window).

Whenever TextBoxWithEllipsis has the focus, the ellipsis is temporarily disabled so the user can edit, select, or scroll the full text. This behavior is built into the control.

Implementation

The code assumes that if TextBox.ViewportWidth + FudgePix < TextBox.ExtentWidth, the text doesn't fit and an ellipsis is needed. The test is made in the LayoutUpdated event handler. If the text doesn't fit, a binary (or bisection) search is performed by setting the length of the Text property halfway between the last length known to fit and the last length known to be too long. Changing the Text property raises the LayoutUpdated event again, causing the logic to iterate until the maximum length substring of LongText that will fit is found.

The OnTextChanged() method is overridden to prevent the TextChanged event from being raised when the Text property is changed internally (e.g., in the LayoutUpdated handler). Setting Text externally and typing/pasting into the control raises the event as expected.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
Mark Lauritsen has been a software developer since 1983, starting at IBM and using a variety of languages including PL/1, Pascal, REXX, Ada, C/C++ and C#. Mark currently works at a midstream energy company developing Windows services and desktop applications in C#.

Comments and Discussions

 
QuestionI`m constantly getting into the resize method Pin
Member 1483768420-May-20 6:42
Member 1483768420-May-20 6:42 
AnswerRe: I`m constantly getting into the resize method Pin
rugyi26-Jan-24 1:50
rugyi26-Jan-24 1:50 
QuestionBinding working until trimming occurs Pin
jcfrig3-Dec-19 9:55
jcfrig3-Dec-19 9:55 
QuestionHow to do the same thing for WPF TextBlock Pin
madufit128-Jun-17 1:30
madufit128-Jun-17 1:30 
AnswerRe: How to do the same thing for WPF TextBlock Pin
Member 1483768420-May-20 9:17
Member 1483768420-May-20 9:17 
GeneralRe: How to do the same thing for WPF TextBlock Pin
Phil Jollans22-Aug-20 2:33
Phil Jollans22-Aug-20 2:33 
SuggestionWPF TextBox with Ellipsis (for Windows 8.1 App) Pin
Member 101510638-Apr-14 21:55
Member 101510638-Apr-14 21:55 
I had to create such a textbox with the ellipsis for a Windows 8.1 App. The closest I came to the solution was through this post. I am very thankful for that and I want to post my solution (which is based on this concept) for people to use.

C#
#region Ellipsis

//If the user clicks on the box to type (got focus)
private void frontValueBox_GotFocus(object sender, RoutedEventArgs e)
{
    //Gets the cursor position to where the user clicks on the box
    int index = frontValueBox.SelectionStart;
    //the text in the textbox gets replaced with the uncut text
    frontValueBox.Text = _text;
    //Sets the cursor position to where the user click on the box
    frontValueBox.SelectionStart = index;
}

//If the user is done typing and loses the focus of the box
private void frontValueBox_LostFocus(object sender, RoutedEventArgs e)
{
    //as soon as the user loses the focus of the box, the text gets replaced with the cut version
    Text = frontValueBox.Text;
    ToolTipService.SetToolTip(frontValueBox, _text);
}

//Calculate the width of the text in DIP (Device independent pixels)
private double stringWidth(string s, double fontSize)
{
    if (s == " ")
        s = "\u00a0";

    TextBlock t = new TextBlock()
    {
        FontSize = fontSize,
        Text = s
    };
    t.Measure(new Size(double.MaxValue, double.MaxValue));
    return t.ActualWidth;
}

//Calculates the necessary text length, cuts it and places it into the textbox
private string CutTextToWidth(string text, double fontSize, double width)
{
    //used to calculate the text into the right size to fit 
    bool validArea = false; 
    //There is a slight difference in width of each letter. this prevents any unwanted effects of not fitting
    //Use *1.1 to increase the valid width by 10%
    double CharDiffLength = (stringWidth("M", fontSize) - stringWidth("|", fontSize)); //*1.1
    //this value gets returned at the end (shortened text)
    string shortText = text; 
    //last length which didn't fit into the box
    int LastLongLen = text.Length;
    //last length which fit into the box
    int LastFitLen = 0;

    if (stringWidth(text, fontSize) < width)
    {
        shortText = text;
    }
    else
    {
        //repeat calculation until textlength fits into validArea
        while (!validArea)
        {
            if (width < stringWidth(shortText, fontSize))
            {
                //text is too long (does not fit)
                LastLongLen = shortText.Length;
            }
            else
            {
                //text is not too long (fits)
                LastFitLen = shortText.Length;
            }

            int newLen = (LastFitLen + LastLongLen) / 2;

            if (shortText.Length != newLen)
            {
                //save the shortened text
                shortText = text.Substring(0, newLen) + "\u2026";
            }
            validArea = ((width - 10 < stringWidth(shortText, fontSize)) && (stringWidth(shortText, fontSize) < width));
        }
    }
    //return the shortened text
    return shortText;
}

#endregion

 //Get and set the content of the textbox
#region Text
private string _text;
public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        //Here, the function gets called. Hand over the _text, the fontsize of the box and the width
        //(-25) is the padding to the left and right. Feel free to change that value to suit your needs.
        frontValueBox.Text = CutTextToWidth(_text, frontValueBox.FontSize, frontValueBox.Width - 25);
    }
}
#endregion


You basicly only need "stringWidth" "CutTextToWidth(yourText, yourFontSize, yourTextBoxWidth)" and the _text property at the end (in the region "Text"). To change the textbox from outside, you can use it like this (for example):

Let's say you have a button. Whenever you click this button, the text in another textbox gets written into the "shortened" textbox and the text gets cut.

C#
private void Button_Click(object sender, RoutedEventArgs e)
        {
            //Text is the public string (the property) which initializes everything.
            //testText.Text is the textbox from where you get your text
            Text = testText.Text;
        }


I hope I'm allowed to post that here. Hopefully this will help someone someday. Thank you again for this great idea to cut the text!

Greetings
QuestionVery nice, Here is another simple option [ 2 ] Pin
CS Rocks24-Feb-12 5:36
CS Rocks24-Feb-12 5:36 
AnswerRe: Very nice, Here is another simple option [ 2 ] Pin
MarkLTX24-Feb-12 14:51
MarkLTX24-Feb-12 14:51 
AnswerRe: Very nice, Here is another simple option [ 2 ] Pin
Member 1068555530-Apr-15 22:46
Member 1068555530-Apr-15 22:46 
BugCan't bind to LongText property Pin
tlhIn`toq18-Jan-12 7:59
tlhIn`toq18-Jan-12 7:59 
GeneralRe: Can't bind to LongText property Pin
MarkLTX18-Jan-12 14:41
MarkLTX18-Jan-12 14:41 
SuggestionVery nice, Here is another simple option Pin
tal_segal16-Jan-12 20:13
tal_segal16-Jan-12 20:13 
GeneralRe: Very nice, Here is another simple option Pin
MarkLTX17-Jan-12 14:53
MarkLTX17-Jan-12 14:53 
GeneralMy vote of 5 Pin
rctaubert4-Jan-12 3:46
rctaubert4-Jan-12 3:46 
GeneralRe: My vote of 5 Pin
MarkLTX13-Jan-12 1:20
MarkLTX13-Jan-12 1:20 
GeneralRe: My vote of 5 Pin
rctaubert13-Jan-12 2:05
rctaubert13-Jan-12 2:05 
GeneralRe: My vote of 5 Pin
MarkLTX13-Jan-12 4:07
MarkLTX13-Jan-12 4:07 
GeneralMy vote of 5 Pin
Filip D'haene3-Jan-12 19:05
Filip D'haene3-Jan-12 19:05 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.