WPF TextBox con adornos de recorte y validación de elipsis
ellipsis adorner (0)
Actualmente estoy trabajando en un control de usuario para agregar un poco más de funcionalidad a un TextBox:
- Recorte de puntos suspensivos si el texto es demasiado grande y TextBox ha perdido el foco
- Etiqueta en frente del TextBox
- Validación por error
Encontré un ejemplo para la elipsis. Este ejemplo almacena el valor actual de TextBox en una propiedad de dependencia y establece la propiedad TextBox.Text si el elemento obtuvo / perdió el foco.
Aquí mi código xaml de mi control de usuario:
<UserControl x:Class="WpfTextBoxEllipsis.EditUC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Name="EDIT"
Loaded="EditUC_OnLoaded">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Label" Margin="10,0" Grid.Column="0" VerticalAlignment="Center"/>
<TextBox Name="Box" Grid.Column="1"
LostFocus="BoxLostFocus"
GotFocus="BoxGotFocus"
LayoutUpdated="BoxOnLayoutUpdated"/>
</Grid>
El código detrás de mi control de usuario:
public partial class EditUC : UserControl
{
private string textvalue;
public EditUC()
{
InitializeComponent();
}
internal UpdateSourceTrigger UpdateTrigger { get; set; }
#region Text property
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(EditUC),
new FrameworkPropertyMetadata(string.Empty)
{
DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged,
BindsTwoWayByDefault = true
});
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
#endregion
#region Ellisped TextBox
public string TextValue
{
get
{
return this.textvalue;
}
set
{
if (this.textvalue == value)
{
return;
}
this.textvalue = value;
this.Box.Text = this.CutTextToWidth(this.textvalue);
}
}
private string CutTextToWidth(string value)
{
if (string.IsNullOrEmpty(value))
{
return value;
}
var width = this.Box.ActualWidth - 25;
var validArea = false;
var shortText = value;
var lastlong = value.Length;
var lastfit = 0;
if (this.StringWidth(value) < width)
{
shortText = value;
}
else
{
while (!validArea)
{
if (width < this.StringWidth(shortText + "/u2026"))
{
lastlong = shortText.Length;
}
else
{
lastfit = shortText.Length;
}
int newLen = (lastfit + lastlong) / 2;
if (shortText.Length != newLen)
{
shortText = value.Substring(0, newLen);
}
else
{
shortText = value.Substring(0, lastfit);
break;
}
var w = this.StringWidth(shortText + "/u2026");
validArea = (width - 10 < w) && (w < width);
}
shortText += "/u2026";
}
return shortText;
}
private void BoxGotFocus(object sender, RoutedEventArgs e)
{
int index = this.Box.SelectionStart;
this.Box.Text = this.textvalue;
this.Box.SelectionStart = index;
this.Box.TextChanged += this.BoxOnTextChanged;
}
private void BoxOnTextChanged(object sender, TextChangedEventArgs args)
{
if (sender != this.Box || args.Changes.Count <= 0 || this.UpdateTrigger != UpdateSourceTrigger.PropertyChanged)
{
return;
}
this.UpdateVM();
}
private void UpdateVM()
{
this.Text = this.Box.Text;
////var exp = BindingOperations.GetBindingExpression(this, TextProperty);
////if (exp != null)
////{
//// exp.UpdateSource();
//// bool he = exp.HasError;
////}
}
private void BoxLostFocus(object sender, RoutedEventArgs e)
{
this.Box.TextChanged -= this.BoxOnTextChanged;
this.UpdateVM();
this.TextValue = this.Box.Text;
ToolTipService.SetToolTip(this.Box, this.textvalue);
}
private double StringWidth(string s)
{
if (s == " ")
{
s = "/u00a0";
}
var formattedText = new FormattedText(
s,
CultureInfo.CurrentUICulture,
FlowDirection.LeftToRight,
new Typeface(this.Box.FontFamily, this.Box.FontStyle, this.Box.FontWeight, this.Box.FontStretch),
this.Box.FontSize,
Brushes.Black);
return formattedText.Width;
}
private void BoxOnLayoutUpdated(object sender, EventArgs e)
{
if (!this.Box.IsFocused)
{
var width = this.StringWidth(this.Box.Text);
if (width > this.Box.ActualWidth || (width + 10 < this.Box.ActualWidth && this.Box.Text != this.TextValue))
{
this.Box.Text = this.CutTextToWidth(this.TextValue);
}
}
}
#endregion
private void EditUC_OnLoaded(object sender, RoutedEventArgs e)
{
this.TextValue = this.Text;
var exp = BindingOperations.GetBindingExpression(this, TextProperty);
var parent = exp != null ? exp.ParentBinding : null;
this.UpdateTrigger = parent != null ? parent.UpdateSourceTrigger : UpdateSourceTrigger.Default;
if (this.UpdateTrigger == UpdateSourceTrigger.Default)
{
var def = TextProperty.GetMetadata(this) as FrameworkPropertyMetadata;
if (def != null)
{
this.UpdateTrigger = def.DefaultUpdateSourceTrigger;
}
}
this.AddHandler(Validation.ErrorEvent, new RoutedEventHandler(OnErrorEvent));
}
private void OnErrorEvent(object sender, RoutedEventArgs routedEventArgs)
{
if (sender == null)
{
return;
}
}
}
Con el siguiente código, obtengo un borde rojo alrededor del control de usuario completo en caso de errores de validación.
<local:EditUC Text="{Binding Text, ValidatesOnDataErrors=True}"/>
Pero solo quiero tener un error adorner alrededor del TextBox.
- ¿Alguien tiene una solución para mi problema?
Configuro manualmente el contenido de la "Caja" de TextBox.
- ¿Existe la posibilidad de utilizar TextProperty vinculante para Box.Text?
- ¿Esto ayudará a
Por último, pero no menos importante, mi modelo de vista:
public class MainVM : INotifyPropertyChanged, IDataErrorInfo
{
private string text;
public string Text
{
get
{
return this.text;
}
set
{
if (this.text == value)
{
return;
}
this.text = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public string this[string columnName]
{
get
{
return this.Validate(columnName);
}
}
public string Error
{
get
{
return "asd";
}
}
private string Validate(string properyName)
{
string msg = string.Empty;
switch (properyName)
{
case "Text":
msg = this.Text == "Valid" ? string.Empty : "Error";
break;
}
return msg;
}
}
Muchas gracias por tu ayuda.
Saludos cordiales Cristianos