WPF TextBox MaxLength - есть ли способ связать это с максимальной длиной проверки данных в связанном поле? - PullRequest
10 голосов
/ 05 января 2012

ViewModel:

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

XAML:

<TextBox Text="{Binding SomeProperty}" MaxLength="50" />

Есть ли способ избежать установки MaxLength TextBox, чтобы он соответствовал моей ViewModel (которая может измениться, так как он находится в другой сборке) и автоматически установил максимальную длину на основе требования StringLength?

Ответы [ 4 ]

17 голосов
/ 08 февраля 2013

Я использовал Поведение , чтобы подключить TextBox к атрибуту проверки его привязанного свойства (если оно есть). Поведение выглядит так:

/// <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;
                }
            }
        }
    }
}

Вы можете видеть, что поведение просто извлекает контекст данных текстового поля и его связующее выражение для «Текст». Затем он использует отражение, чтобы получить атрибут "StringLength". Использование так:

<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>

Вы также можете добавить эту функцию, расширив TextBox, но мне нравится использовать поведения, потому что они модульные.

1 голос
/ 05 января 2012

Хотя я не собираюсь писать код полностью сам, одна идея состоит в том, чтобы создать свой собственный MarkupExtension, который будет принимать имя свойства и размышлять над поиском StringLengthAttribute.

Если атрибут существует, попытайтесь привязать цель к этому значению (используя отражение). Если нет, то привяжите 0 к целевому значению (0 по умолчанию, т.е. не макс.).

0 голосов
/ 10 мая 2015

Расширение Markup - определенно лучший способ.Я создаю подкласс BindingDecoratorBase под названием Binding, который имеет свойство зависимости DataType модели.Поскольку MarkupExtensions создаются во время InitializeComponent (), нет способа определить DataContext, так как он еще не был установлен.

Предоставление типа модели разрешает рефлексивный доступ к атрибутам, определенным в модели.Это позволяет:

  • Настройка MaxLength для TextBoxes.
  • Настройка StringFormat для TextBlocks.
  • Настройка конвертера по умолчанию в зависимости от типа данных элемента.
  • Добавление обязательной проверки.Использование привязки ValidationRules или установка ValidatesOnDataErrors.

Разметка выглядит следующим образом: Text = "{PO: Binding DataType = model: modAccount, Path = SubAccount}"

Форматирование,MaxLength и Conversion объединены в один пакет без необходимости что-либо менять при изменении классов модели.

0 голосов
/ 05 января 2012

Один из способов сделать это - создать свойство в той же модели представления с именем SomePropertyMaxLength, а затем связать свойство MaxLength с этим свойством.

<TextBox Text="{Binding SomeProperty}" MaxLength="{Binding SomePropertyMaxLength}"/>
...