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);
}
}
}
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.
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.
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.
Related
I need to read values like Padding etc. stored in Style of Button (or other FrameworkElement). How to do this?
For example if I make a Style for Button like this:
Style style = new Style(typeof(Button));
style.Setters.Add(new Setter(Button.HeightProperty, 70));
MyButton.Style = style;
So... How I can read later for example the Setter HeightProperty? And what about in case below? How to get Padding?
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="ContentPresenter"
Padding="11,15,7,0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
I have tried to get information by
Style ButtonStyle = MyButton.GetStyle();
but after this I don't get it at all how to continue.
In first case you can get the currently applied value using the GetValue mehtod:
var value = (double)MyButton.GetValue(Button.HeightProperty);
Or even more simply:
var value = MyButton.Height;
In second case the problem is a bit more complicated, as the Padding is part of the template itself, not the button. To access it, you will need the following helper method:
public IEnumerable<TChildType> FindChildren<TChildType>(DependencyObject parent)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is TChildType typedChild)
{
yield return typedChild;
}
foreach (var nestedChild in FindChildren<TChildType>(child))
{
yield return nestedChild;
}
}
}
This traverses the VisualTree under a parent and searches for descendants of certain type. We can use it like this:
var contentPresenter = FindChildren<ContentPresenter>(MyButton).First();
Debug.WriteLine(contentPresenter.Padding);
Make sure to call the FindChildren method only after the page is actually loaded (for example in the Page.Loaded event handler, or OnNavigatedTo), as in the Page constructor, the template children do not exist yet and the helper would return no children.
I'm trying to create a UserControl that acts as a sort of segmented progress bar. Input would be a collection of objects, each object would have a category, a duration property, and status property. The UserControl should stretch the width and height of the parent control. Each item in the collection should represent a segment of the progress bar; color of the segment is related to the status, the width of the segment is related to the duration, and the text overlaid on the segment would be related to the category or something.
Example custom progress bar:
The text might be the collection item's ID, the top segment color would be related to status, the bottom color would be related to the category, and the width related to the duration.
Some of the options I've considered:
Make a stackpanel and somehow define each items width and wrap the whole thing in a viewbox to make it stretch the height and width. How could I control the text size, how do I make the content fit the height, how do I bind a stackpanel to a collection?
Make an attached property for a grid control that would dynamically create columns and map the collection items to the grids. Seems like a lot of work and I'm hoping theres a simpler solution since my requirements are pretty specific.
Maybe theres a way to override a uniform grid to make it non-uniform?
Maybe I should just go all code-behind and draw rectangles by iterating through my collection?
Either way, I am crossing my fingers that somebody might know a simple solution to my problem.
Here is a full working proposition of solution to the custom progress bar.
Code is here : http://1drv.ms/1QmAVuZ
1 . If all the steps are not the same width, I prefer to use Grid with columns and different widths
The columns are built dynamically based upon following class :
public class StepItem
{
public int Length { get; set; }
public int Index { get; set; }
public String Label { get; set; }
public Brush Brush { get; set; }
}
2. I chose to implement a CustomControl and inherit of ItemsControl
CustomControl because I don't want to take care of implementing of the parts of the template of the Progressbar.
ItemsControl because :
-I want to provide to ItemsSource property a collection of StepItems
-ItemsControl can have some DataTemplate as template for each item
-ItemsControl can have any Panel like Grid as template presenting the collection of items
3. The component has template in Generic.xaml
-layoutGrid wil have the "continuous rainbow"
-overlayGrid will be displayed partially over the steps depending on progression or totally over (if no progress)
-ItemsPresenter will present the collection of DataTemplates corresponding to each StepItem
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ProgressItemsControl}">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="layoutGrid">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Grid x:Name="overlayGrid" Width="100" HorizontalAlignment="Right" Background="White"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
4. Customisation of the ItemsPanel to use a Grid (instead of vertical layout)
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate >
<Grid x:Name="stepsGrid" IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
5. In code behind of components, setting of the column width
int i = 0;
foreach (StepItem stepItem in ItemsSource)
{
total += stepItem.Length;
var columnDefinition = new ColumnDefinition() { Width = new GridLength(stepItem.Length, GridUnitType.Star) };
stepsGrid.ColumnDefinitions.Add(columnDefinition);
Grid.SetColumn(stepsGrid.Children[i], stepItem.Index);
i++;
}
6. Code behind for declaring Dependency properties that can be monitored
(excerpt)
public int Value
{
get { return (int)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
// Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(ProgressItemsControl), new PropertyMetadata(0));
7. Usage of the component
<local:CustomProgressBar
x:Name="customProgressBar1"
HorizontalAlignment="Left" Height="50" Margin="32,49,0,0"
VerticalAlignment="Top" Width="379"/>
8. Feeding the component with data
private List<StepItem> stepItems = new List<StepItem>{
new StepItem{
Index=0,
Label="Step1",
Length=20,
Brush = new SolidColorBrush(Color.FromArgb(255,255,0,0)),
new StepItem{
Index=4,
Label="Step5",
Length=25,
Brush = new SolidColorBrush(Color.FromArgb(255,0,128,0)),
},
};
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
progressItemsControl1.ItemsSource = stepItems;
}
Regards
I am making this app for windows phone 7, what I do is retrieve all the images from camera roll, saved pictures and other folder and display them in the listbox inside a wrap panel so they are displayed side by side....the thumbnail of the images is actually displayed hear.....
but as the number of images are increasing UI gets very slow and scrolling takes time...
I read many post and other question I think data virtualization or lazy loading is what I need but I am not understanding how can I use it, I saw the post from shawn oster and peter torr.....
I use a backgroundworker to load the images...
here's how...
void backroungWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.BeginInvoke(() =>
{
foreach (string fileName in fileStorage.GetFileNames("images//*.*"))
{
if (fileName == null)
break;
string filepath = System.IO.Path.Combine("images", fileName);
try
{
using (IsolatedStorageFileStream imageStream = fileStorage.OpenFile(filepath, FileMode.Open))
{
var imageSource = PictureDecoder.DecodeJpeg(imageStream);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(imageStream);
var item = new ImageToName { bmp = bitmapImage, FileName = fileName };
vltBitmapImage.Add(item);
imageStream.Dispose();
imageStream.Close();
}
}
catch
{
Exception x = new Exception();
}
}
if (vltBitmapImage.Count() != 0)
{
lone.Visibility = Visibility.Collapsed;
this.vaultbox.ItemsSource = vltBitmapImage;
}
else
lone.Visibility = Visibility.Visible;
});
}
any help is greatly appreciated.....
sorry for being a noob...
Try this sample from code project, it explain how it work and comes with a full sample project
See: Loading Data when the User scrolls to the end of the list
If you want to add a Lazy load to a listbox you must setup a listner for your list box and change the way you load data into your data model, so first lest set up at the XAML code for a listbox:
In your page resource add this style, and note the loaded event its include ("ScrollViewer_Loaded"):
...
<phone:PhoneApplicationPage.Resources>
<Style x:Key="BusinessListBoxStyle"
TargetType="ListBox">
<Setter Property="Background"
Value="Transparent" />
<Setter Property="Foreground"
Value="{StaticResource PhoneForegroundBrush}" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="BorderThickness"
Value="0" />
<Setter Property="BorderBrush"
Value="Transparent" />
<Setter Property="Padding"
Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer x:Name="scrollViewer"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
Loaded="ScrollViewer_Loaded">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</phone:PhoneApplicationPage.Resources>
...
The add a reference to your style for the listbox, and bind your itemsSource to a list of items from your viewModel.
...
<ListBox x:Name="myList"
ItemsSource="{Binding myDataSource}"
Style="{StaticResource BusinessListBoxStyle}">
...
Next you have to set some code that loads data into the datamodel, when you reach the end of the list element in the list (the datamodel start loading more data into the mode, adds more items) Lazyloading!!
The way i normaly do this is by listing to the vertical offset of the listbox's scrollbar, and if its is about 1/4 from the edge i starts loading more items into the datamodel.
In the ScrollViewer loaded handler i set up at VertialOffset listener, by using the DependencyProperty, see code below:
public static readonly DependencyProperty ListVerticalOffsetProperty =
DependencyProperty.Register(
"ListVerticalOffset",
typeof(double),
typeof(MyPage),
new PropertyMetadata(new PropertyChangedCallback(OnListVerticalOffsetChanged))
);
private ScrollViewer _listScrollViewer;
private void ScrollViewer_Loaded(object sender, RoutedEventArgs e)
{
_listScrollViewer = sender as ScrollViewer;
Binding binding = new Binding();
binding.Source = _listScrollViewer;
binding.Path = new PropertyPath("VerticalOffset");
binding.Mode = BindingMode.OneWay;
this.SetBinding(ListVerticalOffsetProperty, binding);
}
public double ListVerticalOffset
{
get { return (double)this.GetValue(ListVerticalOffsetProperty); }
set { this.SetValue(ListVerticalOffsetProperty, value); }
}
private double _lastFetch;
private static void OnListVerticalOffsetChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyPage page = obj as MyPage;
ScrollViewer viewer = page._listScrollViewer;
if (viewer != null)
{
if (page._lastFetch < viewer.ScrollableHeight)
{
// Trigger within 1/4 the viewport.
if (viewer.VerticalOffset >= (viewer.ScrollableHeight - (viewer.ViewportHeight / 4)))
{
page._lastFetch = viewer.ScrollableHeight;
MyViewModel _tmpviewmodel = page.DataContext as MyViewModel;
if ((_tmpviewmodel != null) && (_tmpviewmodel.HasMoreItems))
_tmpviewmodel.GetMoreItems();
}
}
}
}
Note here i make use of a MyViewModel, that holds all the items the listbox i binded to, and has methods for load items from a database, the isolatedstore, the web or what ever your needs are.
You just have to find your way of only load a part of your data into the viewmodel. I your case i will first load a list of all the files you need to load (that is just to retreive the list from GetFileNames in the IsoLatedStore). Then mayby only loads 20 pics at the time!
I have a textbox containing a decimal value on my interface that I want to clear whenever the user selects it.
However, if the user doesn't make any changes and selects another interface element I need the text to revert to whatever it was previous to the clear.
So far I have the the following style:
<Style x:Key="CustomTextBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag}"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Text" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
And then the following to use the style:
<TextBox Style="{DynamicResource CustomTextBoxStyle}"
Tag="{Binding myDecimalValue, StringFormat=#.###}"
TabIndex="1" />
However, in this scenario the value reverts back to what it was even when the user enters a new value.
Can anyone tell me the best way to go about achieving this?
Thanks,
The solution here is not to hide the text but to store it in a variable for use later. In C# the code would be something like:
string _originalValue;
public OnFocus(){
_originalValue = TextBox.Text;
TextBox.Text = "";
}
public LostFocus(){
if(TextBox.Text == "")
TextBox.Text = _originalValue;
}
You could set the forground colour to transparent to hide the text if that's appropriate.
If you actually want to delete the text you should do what Ryan Amies is suggesting on the viewmodel which you should be able to get through the datacontext.
Thanks for the help, but I was able to achieve what I was looking for and adhere to MVVM principles by using the AttachedProperty described at the following:
https://stackoverflow.com/a/7972361/1466960
This allowed me to bind the IsFocused property to a value in my view model and proceed in a similar fashion to the one described by Ryan Amies.
View Model:
bool isFocused = false;
double original;
public bool IsFocused
{
get
{
return isFocused;
}
set
{
isFocused = value;
if (isFocused)
{
original = current;
current = "";
}
else
{
if (HundredPercentLine == "")
current = original;
}
OnPropertyChanged(new PropertyChangedEventArgs("IsFocused"));
}
}
I have one TextBlock having width say 100. When the text length is a large one I want to show the characters that is accomodated in that textblock and a (...) button besides the text to specify user that more text is also there. Upon click on that (...) button, the full text will be shown in a separate pop up window.
So i want how the dynamic (...) button will be shown whenever the text length exceed the size of the textblock. Please answer
This isn't exactly what you want, but it's a similar idea and just uses the baked-in stuff:
<TextBlock MaxWidth="200"
Text="{Binding YourLongText}"
TextTrimming="WordEllipsis"
ToolTip="{Binding YourLongText}" />
So you have a TextBlock with a maximum width, and when the text can't fit it displays an ellipsis ("..."). Hovering over the TextBlock with your mouse will show the full text in a ToolTip.
Just experience the same requirement for adding ellipsis on button so adding the solution here
<Style x:Key="editButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center" >
<ContentPresenter.Resources>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"></Setter>
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
Notice the resources in content presenter.
I believe what you want is to set the TextTrimming property. Settng it to WordElilipsis or CharacterEllipsis should provide what you need.
My solution to the problem is probably overkill, but allows for some configuration and control.
I created a behavior that allows me to set the character limit for each binding.
internal class EllipsisStringBehavior
{
public static readonly DependencyProperty CharacterLimitDependencyProperty = DependencyProperty.RegisterAttached("CharacterLimit", typeof(int), typeof(EllipsisStringBehavior), new PropertyMetadata(255, null, OnCoerceCharacterLimit));
public static readonly DependencyProperty InputTextDependencyProperty = DependencyProperty.RegisterAttached("InputText", typeof(string), typeof(EllipsisStringBehavior), new PropertyMetadata(string.Empty, OnInputTextChanged));
// Input Text
public static string GetInputText(DependencyObject dependencyObject)
{
return Convert.ToString(dependencyObject.GetValue(InputTextDependencyProperty));
}
public static void SetInputText(DependencyObject dependencyObject, string inputText)
{
dependencyObject.SetValue(InputTextDependencyProperty, inputText);
}
// Character Limit
public static int GetCharacterLimit(DependencyObject dependencyObject)
{
return Convert.ToInt32(dependencyObject.GetValue(CharacterLimitDependencyProperty));
}
public static void SetCharacterLimit(DependencyObject dependencyObject, object characterLimit)
{
dependencyObject.SetValue(CharacterLimitDependencyProperty, characterLimit);
}
private static void OnInputTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock textblock = (TextBlock)d;
string input = e.NewValue == null ? string.Empty : e.NewValue.ToString();
int limit = GetCharacterLimit(d);
string result = input;
if (input.Length > limit && input.Length != 0)
{
result = $"{input.Substring(0, limit)}...";
}
textblock.Text = result;
}
private static object OnCoerceCharacterLimit(DependencyObject d, object baseValue)
{
return baseValue;
}
}
I then simply add the using to my user control...
<UserControl
xmlns:behavior="clr-namespace:My_APP.Helper.Behavior"
d:DesignHeight="300" d:DesignWidth="300">
...and apply the behavior to the TextBlock control I wish to use it on.
<TextBlock Margin="0,8,0,8"
behavior:EllipsisStringBehavior.CharacterLimit="10"
behavior:EllipsisStringBehavior.InputText="{Binding Path=DataContext.FeedItemTwo.Body, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignSubheadingTextBlock}"
FontSize="14"/>
Hope this helps.