WPF TextBox Validation - PullRequest
       21

WPF TextBox Validation

12 голосов
/ 17 января 2010

У меня есть проверка, подключенная к модели, связанной с контейнером TextBox. Когда впервые открывается окно, появляются ошибки проверки, так как модель пуста, я не хочу видеть ошибки проверки, пока не будет изменена отправка окна или текста в TextBox или если он потерял фокус.

Вот это TextBox:

<TextBox Text="{Binding 
                   Path=Firstname, 
                   UpdateSourceTrigger=PropertyChanged, 
                   ValidatesOnDataErrors=True}"
         Width="124"
         Height="24"/>

Как этого достичь?

Ответы [ 4 ]

22 голосов
/ 23 января 2010

Это действительно зависит от вашей реализации IDataErrorInfo. Если вы основываете его на словаре сообщений об ошибках, вы можете контролировать, когда запускается проверка, которая добавляет этот список. Обычно вы хотели бы сделать это из ваших установщиков свойств (как всякий раз, когда вы вызываете PropertyChange), здесь вызывается CheckValidationState:

    public string this[string columnName]
    {
        get
        {
            return ValidateProperty(columnName);
        }
    }

    public Dictionary<string, string> Errors { get; private set; }

    protected void SetError(string propertyName, string errorMessage)
    {
        Debug.Assert(!String.IsNullOrEmpty(propertyName), "propertyName is null or empty.");
        if (String.IsNullOrEmpty(propertyName))
            return;

        if (!String.IsNullOrEmpty(errorMessage))
        {
            if (Errors.ContainsKey(propertyName))
                Errors[propertyName] = errorMessage;
            else
                Errors.Add(propertyName, errorMessage);
        }
        else if (Errors.ContainsKey(propertyName))
            Errors.Remove(propertyName);

        NotifyPropertyChanged("Errors");
        NotifyPropertyChanged("Error");
        NotifyPropertyChanged("Item[]");
    }

    protected virtual string ValidateProperty(string propertyName)
    {
        return Errors.ContainsKey(propertyName) ? Errors[propertyName] : null;
    }

    protected virtual bool CheckValidationState<T>(string propertyName, T proposedValue)
    {
        // your validation logic here
    }

Затем вы также можете включить метод, который проверяет все ваши свойства (например, во время сохранения):

    protected bool Validate()
    {
        if (Errors.Count > 0)
            return false;

        bool result = true;
        foreach (PropertyInfo propertyInfo in GetType().GetProperties())
        {
            if (!CheckValidationState(propertyInfo.Name, propertyInfo.GetValue(this, null)))
                result = false;
            NotifyPropertyChanged(propertyInfo.Name);
        }
        return result;
    }

UPDATE:

Я бы рекомендовал поместить приведенный выше код в базовый класс ViewModel, чтобы вы могли использовать его повторно. Затем вы можете создать производный класс следующим образом:

public class SampleViewModel : ViewModelBase
{
    private string _firstName;

    public SampleViewModel()
    {
        Save = new DelegateCommand<object>(SaveExecuted);
    }

    public DelegateCommand<object> Save { get; private set; }

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value)
                return;

            CheckValidationState("FirstName", value);

            _firstName = value;
            NotifyPropertyChanged("FirstName");
        }
    }

    public void SaveExecuted(object obj)
    {
        bool isValid = Validate();
        MessageBox.Show(isValid ? "Saved" : "Validation Error. Save canceled"); // TODO: do something appropriate to your app here
    }

    protected override bool CheckValidationState<T>(string propertyName, T proposedValue)
    {
        // your validation logic here
        if (propertyName == "FirstName")
        {
            if (String.IsNullOrEmpty(proposedValue as String))
            {
                SetError(propertyName, "First Name is required.");
                return false;
            }
            else if (proposedValue.Equals("John"))
            {
                SetError(propertyName, "\"John\" is not an allowed name.");
                return false;
            }
            else
            {
                SetError(propertyName, String.Empty); // clear the error
                return true;
            }
        }
        return true;
    }
}

В этом случае я использую DelegateCommand для запуска операции сохранения, но это может быть все, что вызывает метод для сохранения. Эта настройка позволяет исходному пустому состоянию отображаться как действительное в пользовательском интерфейсе, но изменение или вызов Save обновляют состояние проверки. Вы также можете получить гораздо более общее и более сложное представление о том, как на самом деле выполнять проверку, так что не все заканчивается одним методом (здесь с некоторыми предположениями о типе), но это упрощено, чтобы было легче начать с .

7 голосов
/ 18 января 2010

Если вы реализуете IDataErrorInfo, я добился этого, проверив нулевые значения в реализации логики проверки. При создании нового окна проверка на ноль предотвратит срабатывание логики проверки. Например:

public partial class Product : IDataErrorInfo
{
    #region IDataErrorInfo Members

    public string Error
    {
        get { return null; }
    }

    public string this[string columnName]
    {
        get
        {
            if (columnName == "ProductName")
            {
                // Only apply validation if there is actually a value
                if (this.ProductName != null)
                {
                    if (this.ProductName.Length <= 0 || this.ProductName.Length > 25)
                        return "Product Name must be between 1 and 25 characters";
                }
            }

            return null;
        }
    }

    #endregion
}

Кроме того, если вы хотите запустить проверку на TextBox.LostFocus, измените привязку на LostFocus, как показано ниже:

<TextBox Text="{Binding              
               Path=Firstname,              
               UpdateSourceTrigger=LostFocus,              
               ValidatesOnDataErrors=True}"             
     Width="124"             
     Height="24"/>
4 голосов
/ 30 ноября 2012

в вашем файле app.xaml, вам нужно использовать пользовательский стиль текстового поля для проверки текстового поля без каких-либо сторонних компонентов.

enter image description here

<Setter Property="Template">
    <Setter.Value>
        <ControlTemplate TargetType="{x:Type TextBox}">
            <Grid Name="test">
                <Border Background="{StaticResource TextBackColor}"
                        BorderBrush="#FF888888"
                        x:Name="Bd"
                        CornerRadius="1"
                        BorderThickness="1">
                    <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
                </Border>
                <Image  Name="ErrorImage"
                        Width="15"
                        Height="15"
                        Margin="0,0,4,0"
                        Source="Images/validate.png"
                        HorizontalAlignment="Right">
                </Image>
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>
0 голосов
/ 17 января 2010

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

...