простой способ добавить пользовательское правило проверки во все текстовые поля

У меня возникают проблемы с настройкой пользовательского правила проверки для всех моих текстовых полей на странице.

Я знаю, как установить его для одного текстового поля:

<TextBox Margin="79,118,79,121" Name="textBox1" Style="{Binding EmptyTextboxValidator, UpdateSourceTrigger=PropertyChanged}">
    <TextBox.Text>
        <Binding Path="Text" UpdateSourceTrigger="PropertyChanged">
            <Binding.ValidationRules>
                <validators:StringRangeValidationRule MinimumLength="1" ErrorMessage="Text is te kort" />
            </Binding.ValidationRules>
        </Binding>
    </TextBox.Text>
</TextBox>

Однако, если мне нужно добавить этот код для каждого отдельного текстового поля, я не уверен, что это так же полезно, так как было бы очень много копий и вставок. Должен же быть более простой способ?

если я много искал, но я не смог найти пример, чтобы установить его для всех текстовых полей, только примеры, чтобы установить его для 1 одного текстового поля (как показано выше)

вот код стиля текстового поля:

<Application.Resources>
    <Style x:Key="EmptyTextboxValidator" TargetType="{x:Type TextBox}">
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate>
                    <DockPanel LastChildFill="True">
                        <Border BorderBrush="Red" BorderThickness="3">
                            <AdornedElementPlaceholder Name="MyAdorner" />
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="true">
                <Setter Property="ToolTip" 
                        Value="{Binding RelativeSource={RelativeSource Self}, 
                        Path=(Validation.Errors)[0].ErrorContent}"/>
            </Trigger>
        </Style.Triggers>
    </Style>
</Application.Resources>

и код правила проверки:

using System;
using System.Windows.Controls;
using System.Globalization;

namespace MyValidators
{
    public class StringRangeValidationRule : ValidationRule
    {
        private int _minimumLength = 1;
        private string _errorMessage;

        public int MinimumLength
        {
            get { return _minimumLength; }
            set { _minimumLength = value; }
        }

        public string ErrorMessage
        {
            get { return _errorMessage; }
            set { _errorMessage = value; }
        }

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            ValidationResult result = new ValidationResult(true, null);
            string inputString = (value ?? string.Empty).ToString();
            if (string.IsNullOrEmpty(inputString) || inputString.Length < MinimumLength)
            {
                result = new ValidationResult(false, this.ErrorMessage);
            }
            return result;
        }
    }
}

модель представления:

public class Data : INotifyPropertyChanged
{
    private int id = -1;
    private bool updated = true;
    private string text;
    private string name;

    public int ID
    {
        get
        {
            return id;
        }
        set
        {
            id = value;
            OnPropertyChanged("ID");
        }
    }

    public bool Updated
    {
        get
        {
            return updated;
        }
        set
        {
            updated = value;
        }
    }

    public string Text
    {
        get
        {
            return text;
        }
        set
        {
            text = value;
            OnPropertyChanged("Text");
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        updated = true;
    }
    #endregion
}

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

Также может быть способ просто добавить поле в текстовое поле, например:

<TextBox ValidateStringLength="true"/>

или даже лучше, если бы я мог просто указать целочисленный параметр для минимальной длины строки:

<TextBox ValidateStringLength="5"/>

Либо один из двух (устанавливая его так, как я, но для всех текстовых полей сразу), либо пример, который я продемонстрировал выше, будет действительно оценен.


person Vincent    schedule 06.12.2013    source источник
comment
Создайте настраиваемый элемент управления, представляющий собой текстовое поле и вашу проверку. Затем повторно используйте это.   -  person Richard    schedule 06.12.2013
comment
вы проверяли, используя IDataErrorInfo для проверки? это больше mvvm, но красиво и легко :)   -  person blindmeis    schedule 06.12.2013
comment
@blindmeis, ну, почти все приложения wpf, которые я делаю, я пытаюсь реализовать mvvm, так что это было бы неплохо. Я посмотрю на это.   -  person Vincent    schedule 06.12.2013
comment
@Richard, я забыл о пользовательских элементах управления, это может быть хорошим альтернативным решением.   -  person Vincent    schedule 06.12.2013


Ответы (1)


Решение называется BindingGroup. Взгляните на этот код:

<StackPanel Name="stackPanel1">
  <StackPanel.BindingGroup>
    <BindingGroup NotifyOnValidationError="True">
      <BindingGroup.ValidationRules>
        <src:ValidateDateAndPrice ValidationStep="ConvertedProposedValue" />
      </BindingGroup.ValidationRules>
    </BindingGroup>
  </StackPanel.BindingGroup>

  <HeaderedContentControl Header="Price">
    <TextBox Name="priceField"  Width="150">
      <TextBox.Text>
        <Binding Path="Price" Mode="TwoWay" />
      </TextBox.Text>
    </TextBox>
  </HeaderedContentControl>

  <HeaderedContentControl Header="Date Offer Ends">
    <TextBox Name="dateField" Width="150" >
      <TextBox.Text>
        <Binding Path="OfferExpires" StringFormat="d" />
      </TextBox.Text>
    </TextBox>
  </HeaderedContentControl>

  <StackPanel Orientation="Horizontal">
    <Button IsDefault="True" Click="Submit_Click">_Submit</Button>
    <Button IsCancel="True" Click="Cancel_Click">_Cancel</Button>
  </StackPanel>
</StackPanel>

Все текстовые поля имеют ValidateDateAndPrice ValidationRule.

И вы также можете контролировать, когда изменения должны быть сохранены.

private void Submit_Click(object sender, RoutedEventArgs e)
{
    if (stackPanel1.BindingGroup.CommitEdit())
    {
        MessageBox.Show("Item submitted");
        stackPanel1.BindingGroup.SubmitEdit();
    }
}

В этом случае изменения отправляются при нажатии кнопки.

Изменить:

Я приведу вам пример, как заставить его работать. Я использовал ваш код для создания примера.

<Window>
    ....
    <Window.Resources>
        <Style TargetType="{x:Type TextBox}">
            <Setter Property="Validation.ErrorTemplate">
                <Setter.Value>
                    <ControlTemplate>
                        <DockPanel LastChildFill="True">
                            <Border BorderBrush="Blue" BorderThickness="3">
                                <AdornedElementPlaceholder Name="MyAdorner" />
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="Validation.HasError" Value="true">
                    <Setter Property="ToolTip" 
                        Value="{Binding RelativeSource={RelativeSource Self}, 
                        Path=(Validation.Errors)[0].ErrorContent}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>

    <Window.BindingGroup>
        <BindingGroup>
            <BindingGroup.ValidationRules>
                <local:StringRangeValidationRule MinimumLength="5" ErrorMessage="Text too long!" />
            </BindingGroup.ValidationRules>
        </BindingGroup>
    </Window.BindingGroup>

    <Grid Margin="10" >
        <TextBox Margin="100, 5, 0, 0" HorizontalAlignment="Left" VerticalAlignment="Top"
                 Text="{Binding Name}" Validation.ValidationAdornerSiteFor="{Binding ElementName=window}">
        </TextBox>
        <Button Content="Save" Click="Button_Click" Margin="0, 5, 100, 0" HorizontalAlignment="Right" VerticalAlignment="Top"/>
    </Grid>
</Window>

Вам нужно иметь свои шаблоны и стили в окне, чтобы заставить его работать.

Это код позади:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new Data() { Name = "Hello World!" };
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        window.BindingGroup.CommitEdit();
    }
}

public class Data : INotifyPropertyChanged
{
    private int id = -1;
    private bool updated = true;
    private string text;
    private string name;

    public int ID
    {
        get
        {
            return id;
        }
        set
        {
            id = value;
            OnPropertyChanged("ID");
        }
    }

    public bool Updated
    {
        get
        {
            return updated;
        }
        set
        {
            updated = value;
        }
    }

    public string Text
    {
        get
        {
            return text;
        }
        set
        {
            text = value;
            OnPropertyChanged("Text");
        }
    }

    public string Name
    {
        get
        {
            return name;
        }
        set
        {
            name = value;
            OnPropertyChanged("Name");
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        updated = true;
    }

    #endregion
}

public class StringRangeValidationRule : ValidationRule
{
    private int _minimumLength = 1;
    private string _errorMessage;

    public int MinimumLength
    {
        get { return _minimumLength; }
        set { _minimumLength = value; }
    }

    public string ErrorMessage
    {
        get { return _errorMessage; }
        set { _errorMessage = value; }
    }

    public override ValidationResult Validate(object value, CultureInfo cultureInfo)
    {
        BindingGroup group = (BindingGroup)value;
        var item = group.Items[0];
        string inputString = (group.GetValue(item, "Name") ?? string.Empty).ToString();
        if (true || string.IsNullOrEmpty(inputString) || inputString.Length < MinimumLength)
        {
            return new ValidationResult(false, this.ErrorMessage);
        }

        return ValidationResult.ValidResult;
    }
}

Как видите, я изменил правило проверки и добавил Validation.ValidationAdornerSiteFor, чтобы указать, что TextBox должен отображать ошибку.

Вы должны быть в состоянии скопировать и вставить это, и это будет работать.

person dev hedgehog    schedule 06.12.2013
comment
так что я мог бы применить широкое приложение, широкое окно или ширину страницы? установив его в .resources? или мне нужно разместить его в другом месте? - person Vincent; 06.12.2013
comment
В моем случае это StackPanel, который действует как родитель для всех текстовых полей, которые у меня есть. Да, вам нужно поместить BindingGroup в родителя. Window тоже будет работать, так как Window является родителем всех элементов. Окно является корнем. Но будьте осторожны, если вы поместите его в окно, потому что вы можете в конечном итоге поделиться ValidationRules с текстовыми полями, которые вам не нужны. - person dev hedgehog; 06.12.2013
comment
Кажется, я не могу заставить его работать с окном, не могли бы вы привести пример его создания с окном вместо панели стека? - person Vincent; 06.12.2013
comment
все это в вопросе, вы можете скопировать его оттуда;) также я попытался использовать код с панелью стека, и он говорит, что нет метода SubmitEdit (). (может быть, потому что я использую .net 3.5) - person Vincent; 06.12.2013
comment
Дайте мне ваши данные, пожалуйста. ViewModel тоже отсутствует. - person dev hedgehog; 06.12.2013
comment
хотя я могу использовать это для всех текстовых полей, это не работает для всех, поскольку последнее текстовое поле, объявленное в коде xaml, будет красным при ошибке, даже если это текстовое поле не содержит ошибку, а другое текстовое поле в этом окне. - person Vincent; 09.12.2013