user3701907
user3701907

Reputation: 41

Create a property out of a Class

I have created a class, which shortens a text to fit into a textbox, while also adding "...".

Example:

[ThisTextis]toolong > [ThisTex...]

I'm using Microsoft Visual Studio 2013 on a Windows 8.1 VM (Virtual Machine) and the software is an App.

Now it might not be perfect, but that works all wonderful and fine. What I want to know now is if I can create a boolean property out of that class, so the user can enable/disable it on a textbox in XAML:

<TextBox CutText="True"/>

The problem is that I already use the pre-existing Text property inside the class and my code is not as beautiful as I wish it to be. Anyways, I'd be very glad for any advice and/or help.

Edit: Simply put, I want to create a TextBlock.TextTrimming property for a TextBox, since the already existing property is restricted to TextBlock.

Here is the Class:

 class CutText
    {
        //Create a new instance of a textbox
        TextBox textCut = new TextBox();

        //Reset at start
        public void ResetText(TextBox text)
        {
            //Overwrite textCut with the chosen TextBox
            textCut = text;
            //Handles the possibility of already filled textbox
            if (textCut.Text != "" || textCut.Text != null)
            {
                _text = textCut.Text;
            }
            //Prevents text from being 'Null' 
            else
            {
                _text = "";
            }
        }

        //Cuts text to width of textbox
        private string CutTextToWidth(string text, double fontSize, double width)
        {
            //boolean to check if width of text is correct
            bool validArea = false;
            //comply with difference in width of characters 
            double CharDiffLength = (stringWidth("M", fontSize) - stringWidth("|", fontSize));
            //shortened text
            string shortText = text;
            //last length which was too long
            int LastLongLen = text.Length;
            //last length which fit into textbox
            int LastFitLen = 0; 

            if (stringWidth(text, fontSize) < width)
            {
                shortText = text;
            }
            else
            {
                //repeat until the text fits into the appointed area
                while (!validArea)
                {
                    if (width < stringWidth(shortText, fontSize))
                    {
                        //text is still too long
                        LastLongLen = shortText.Length;
                    }
                    else
                    {
                        //text is not too long
                        LastFitLen = shortText.Length;
                    }

                    int newLen = (LastFitLen + LastLongLen) / 2;

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

        //Calculate the width of the text
        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;
        }

        //(GotFocus) Replaces cut text with full text and places the cursor at the chosen position
        public void GotFocusText()
        {
            int index = textCut.SelectionStart;
            textCut.Text = _text;
            textCut.SelectionStart = index;
        }

        //(LostFocus) Saves cut text into property / empties the textbox if nothing has been written and sets tooltip
        public void LostFocusText()
        {
            if (!string.IsNullOrWhiteSpace(textCut.Text))
            {
                Text = textCut.Text;
            }
            else
            {
                Text = "";
            }
            ToolTipService.SetToolTip(textCut, _text);
        }

        //TextBox.Text Property
        private string _text;
        public string Text
        {
            get { return _text; }
            set
            {
                _text = value;
                //Receive text, fontsize and width of textbox
                textCut.Text = CutTextToWidth(_text, textCut.FontSize, textCut.Width - 25);
            }
        }
    }

Upvotes: 1

Views: 119

Answers (3)

Sheridan
Sheridan

Reputation: 69987

I am sorry to inform you that you have wasted your time. WPF already has that functionality built in. You can set the TextBlock.TextTrimming property to CharacterEllipsis or WordEllipsis and that will automatically trim the overflowing text of the control and add the ellipsis (...). See this simple example:

<TextBlock TextTrimming="CharacterEllipsis" Width="150">
    Lorem ipsum dolor sit amet, consectetur adipisicing</TextBlock>

From the TextTrimming Enumeration page on MSDN:

CharacterEllipsis: Text is trimmed at a character boundary. An ellipsis (...) is drawn in place of remaining text.

WordEllipsis: Text is trimmed at a word boundary. An ellipsis (...) is drawn in place of remaining text.


UPDATE >>>

To answer your question more directly, you should create your code in an Attached Property. That way, you could define a bool Attached Property to use the ellipsis, eg. something like this:

<TextBox TextBoxProperties.CutText="True" />

... where TextBoxProperties would be the name of the class that defines the Attached Property and CutText would be the name of the property itself. See the How to create an Attached Property section from the linked page to find out how to do that. You could use your CutText class from that property.

Upvotes: 4

Mike Eason
Mike Eason

Reputation: 9723

Using the suggestions from the other answers, I have composed a Custom Control which should be useful for you.

public class TrimmedTextBox : TextBox
{
    public bool Trim
    {
        get { return (bool)GetValue(TrimProperty); }
        set { SetValue(TrimProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Trim.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TrimProperty =
        DependencyProperty.Register("Trim", typeof(bool), typeof(TrimmedTextBox), new PropertyMetadata(true));

    public TextTrimming Trimming
    {
        get { return (TextTrimming)GetValue(TrimmingProperty); }
        set { SetValue(TrimmingProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Trimming.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TrimmingProperty =
        DependencyProperty.Register("Trimming", typeof(TextTrimming), typeof(TrimmedTextBox), new PropertyMetadata(TextTrimming.CharacterEllipsis));

    static TrimmedTextBox()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TrimmedTextBox), new FrameworkPropertyMetadata(typeof(TrimmedTextBox)));
    }
}

And the Style:

<Style TargetType="{x:Type local:TrimmedTextBox}"
       BasedOn="{StaticResource {x:Type TextBox}}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:TrimmedTextBox}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ScrollViewer x:Name="PART_ContentHost"/>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
                <Condition Property="IsKeyboardFocused" Value="False"/>
                <Condition Property="Trim" Value="True"/>
            </MultiTrigger.Conditions>

            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="TextBox">
                        <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                            <TextBlock Text="{TemplateBinding Text}" 
                                       TextTrimming="{Binding Trimming, RelativeSource={RelativeSource TemplatedParent}}"/>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </MultiTrigger>
    </Style.Triggers>
</Style>

Usage:

<local:TrimmedTextBox Trim="True" 
                      Text="Le toucan has arrived" 
                      Width="50" 
                      Trimming="CharacterEllipsis"/>

You may have to play around with the style to get the desired look and feel, but the idea remains. This is a Control Template which extends TextBox, which will allow you to set whether you want the textbox to trim the contents and also what type of trimming you need.

For more information on Custom Controls, see here.

Upvotes: 2

David Arno
David Arno

Reputation: 43264

It's neither possible, nor necessary to do what you are trying to do. You cannot add new properties to existing components like that. Also, if you check out this answer to another question, there's a simpler way to achieve what you want to do by defining a WPF style for TextBox.

Upvotes: 0

Related Questions