Reputation: 41
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
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
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
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