WPF TextBox MaxLength — Is there any way to bind this to the Data Validation Max Length on the bound field?

前端 未结 5 623
北海茫月
北海茫月 2020-12-14 23:27

ViewModel:

public class MyViewModel
{
    [Required, StringLength(50)]
    public String SomeProperty { ... }
}

XAML:



        
相关标签:
5条回答
  • 2020-12-14 23:48

    While I'm not going to write the code out completely myself, one idea is to create your own MarkupExtension that will take the property name and reflect over looking for a StringLengthAttribute.

    If the attribute exists, attempt to bind the target to that value (using reflection). If not, then bind 0 to the target value (0 is default, i.e. no max).

    0 讨论(0)
  • 2020-12-14 23:51

    I used a Behavior to connect the TextBox to its bound property's validation attribute (if any). The behavior looks like this:

    /// <summary>
    /// Set the maximum length of a TextBox based on any StringLength attribute of the bound property
    /// </summary>
    public class RestrictStringInputBehavior : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            AssociatedObject.Loaded += (sender, args) => setMaxLength();
            base.OnAttached();
        }
    
        private void setMaxLength()
        {
            object context = AssociatedObject.DataContext;
            BindingExpression binding = AssociatedObject.GetBindingExpression(TextBox.TextProperty);
    
            if (context != null && binding != null)
            {
                PropertyInfo prop = context.GetType().GetProperty(binding.ParentBinding.Path.Path);
                if (prop != null)
                {
                    var att = prop.GetCustomAttributes(typeof(StringLengthAttribute), true).FirstOrDefault() as StringLengthAttribute;
                    if (att != null)
                    {
                        AssociatedObject.MaxLength = att.MaximumLength;
                    }
                }
            }
        }
    }
    

    You can see, the behavior simply retrieves the data context of the text box, and its binding expression for "Text". Then it uses reflection to get the "StringLength" attribute. Usage is like this:

    <UserControl
        xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    
        <TextBox Text="{Binding SomeProperty}">
            <i:Interaction.Behaviors>
                <local:RestrictStringInputBehavior />
            </i:Interaction.Behaviors>
        </TextBox>
    
    </UserControl>
    

    You could also add this functionality by extending TextBox, but I like using behaviors because they are modular.

    0 讨论(0)
  • 2020-12-14 23:51

    The Markup extension is definitely the way to go. I am creating a subclass of BindingDecoratorBase called Binding which has a model DataType dependency property. As MarkupExtensions are created during InitializeComponent() there is no way to determine the DataContext as it will not have been set yet.

    Providing the model type permits reflective access to attributes defined on the model. This permits:

    • Setting MaxLength for TextBoxes.
    • Setting StringFormat for TextBlocks.
    • Setting the default Converter depending on member data type.
    • Adding required validation. Using either the binding's ValidationRules or by setting ValidatesOnDataErrors.

    The markup looks like: Text="{PO:Binding DataType=model:modAccount, Path=SubAccount}"

    Formatting, MaxLength, and Conversion rolled into one package with no need to change anything as the model classes change.

    0 讨论(0)
  • 2020-12-14 23:55

    One way to do it would be to create a property in that same viewmodel called SomePropertyMaxLength and then bind the MaxLength property to that property.

    <TextBox Text="{Binding SomeProperty}" MaxLength="{Binding SomePropertyMaxLength}"/>
    
    0 讨论(0)
  • 2020-12-15 00:04

    Or you can have your model only to accept a max # chars:

    private string _MyText { get; set; }
    public string MyText { get => _MyText; set => _MyText = value?.Substring(0, 
    Math.Min(value.Length, 15)); }
    
    Text="{Binding Path=MyText}"
    
    0 讨论(0)
提交回复
热议问题