wpf textbox ellipsis adorner

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.

  1. ¿Alguien tiene una solución para mi problema?

Configuro manualmente el contenido de la "Caja" de TextBox.

  1. ¿Existe la posibilidad de utilizar TextProperty vinculante para Box.Text?
  2. ¿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