3

検証ルールに関するいくつかの投稿を調べましたが、発生している問題に遭遇していません。

WPFウィンドウのテキストボックスに検証ルールを使用しています。2つのチェックがあります。1つは空のテキストボックス用で、もう1つは正規表現の一致を使用した無効な文字用です。

私の問題はそのようなものです:

私のテキストボックス:

  1. タイプA-動作し、何も表示しない
  2. 空の文字列のバックスペースを押す-動作し、検証エラーメッセージ「すべてのフィールドに値を入力してください」を表示します
  3. タイプ !-動作しません-「無効な文字が見つかりました」と表示されますが、「すべてのフィールドに値を入力してください」と表示されます。
  4. 空の文字列へのバックスペース-「すべてのフィールドに値を入力してください」という最初のエラーが引き続き表示されるため、技術的には機能します。
  5. タイプA-動作します。エラーメッセージはありません
  6. タイプ !-問題ありません。「無効な文字が見つかりました。

同じことが逆に起こります。

窓を開ける

  1. タイプ !-ファイン、「無効な文字が見つかりました。
  2. 空の文字列へのバックスペース-「すべてのフィールドに値を入力してください」ではなく、「無効な文字が見つかりました」と表示されます。

私のコードは次のとおりです。

ProviderNew.xaml:

<Label Name="lblProviderName" Content="Provider Name: " 
                   Grid.Row="1" Grid.Column="0"/>

<TextBox Name="txtBoxProviderName" Margin="2" 
    MaxLength="20" CharacterCasing="Upper"
    Grid.Row="1" Grid.Column="1" LostFocus="TxtBoxProviderNameLostFocus"   TextChanged="TxtBoxProviderNameTextChanged">
                <TextBox.Text>
                    <Binding ElementName="This" Path="ProviderName" UpdateSourceTrigger="PropertyChanged">
                        <Binding.ValidationRules>
                            <Domain:ProviderValidation />
                        </Binding.ValidationRules>
                    </Binding>
                </TextBox.Text>
            </TextBox>

            <TextBlock x:Name="tbNameValidation"  Foreground="Red" FontWeight="Bold" Margin="10,0,0,0"
                       Text="{Binding ElementName=txtBoxProviderName, Path=(Validation.Errors), Converter={StaticResource eToMConverter}}"
                       Grid.Row="1" Grid.Column="2" />

背後にあるプライベートコード-ProviderNew.xaml.cs

public static readonly DependencyProperty NewProviderNameProperty = DependencyProperty.Register("ProviderName",
        typeof (string), typeof(ProviderNew), new UIPropertyMetadata(""));


    public string ProviderName
    {
        get { return (string) GetValue(NewProviderNameProperty); }
        set { SetValue(NewProviderNameProperty, value); }
    }

バリューコンバータークラス

public class ErrorsToMessageConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var sb = new StringBuilder();
        var errors = value as ReadOnlyCollection<ValidationError>;

        if (errors != null && errors.Count > 0)
        {

            foreach (var e in errors.Where(e => e.ErrorContent != null))
            {
                sb.AppendLine(e.ErrorContent.ToString());
            }
        }
        return sb.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

プロバイダークラス

private string _providerName;
    public string ProviderName
    {
        get { return _providerName; }
        set
        {
            _providerName = value;
            RaisePropertyChanged("ProviderName");
        }
    }

private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

検証ルールクラス

public class ProviderValidation : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        var str = value as string;

        if (str != null && !Regex.IsMatch(str, @"^[a-zA-Z0-9]*$"))
        {
           return new ValidationResult(false, "Invalid characters were found.");
        }


        if (String.IsNullOrEmpty(str))
        {
            return new ValidationResult(false, "Please enter a value in all fields.");
        }

        return new ValidationResult(true, null);
    }
}

私が試したのは、LostFocusイベントとTextChangedイベントの両方を設定して、以下を使用して強制的に更新することです。

var expression = txtBoxProviderName.GetBindingExpression(TextBox.TextProperty);
if (expression != null)
    expression.UpdateSource();

Validateメソッドにブレークポイントを設定すると、正しい一致が行われ、正しいValidationResultが返されますが、テキストは正しく更新されません。

私は信じられないほど愚かなことをしていますか?

任意の提案をいただければ幸いです。

編集1。

ええ、MultiDataTriggerとBinding to the textboxesを使用して、これを機能させています。

動作しないのは、最初にウィンドウを表示したときにボタンが有効になっていることです。これにより、ユーザーが空のテキストボックスで[保存]をクリックできるようになる可能性があるため、このボタンは有効にしたくありません。

検証ルールは、ウィンドウが開いたときに最初から直接機能しません。

テキストボックスにフォーカスを設定し、フォーカスが失われたり、間違ったデータが入力されたりすると、検証ルールが開始され、ボタンが無効になります。

ボタンをデフォルトで無効に設定すると、開くと無効になりますが、検証エラーがない場合は有効になりません。

Loadイベントなどで検証ルールのチェックを強制することで、これを機能させることができます。

var expression = txtBoxProviderName.GetBindingExpression(TextBox.TextProperty);
if (expression != null)
    expression.UpdateSource();

しかし、ウィンドウが最初に開いたとき、すぐに検証エラーメッセージ「すべてのフィールドに値を入力してください」が表示されますが、これは見た目があまり好きではありません。

とにかくこれを回避するか、または私は両方の世界の長所を持つことはできません。

これがボタンコードです

<Button x:Name="btnSave" Height="25" Width="100" Content="Save" HorizontalAlignment="Right" Margin="0,0,120,0"
            Grid.Row="2" Click="BtnSaveClick" >
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="IsEnabled" Value="False" />
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=txtBoxProviderName, Path=(Validation.HasError)}" Value="False" />
                            <Condition Binding="{Binding ElementName=txtBoxHelpDesk, Path=(Validation.HasError)}" Value="False" />
                            <Condition Binding="{Binding ElementName=txtBoxRechargeInstructions, Path=(Validation.HasError)}" Value="False" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="IsEnabled" Value="True" />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>     
    </Button>

ありがとう、

ニール

編集2

簡単な質問。RelayCommand名前空間が見つかりませんでした。他のコードを検索すると、RelayCommand:ICommandクラスを実装するMicrosoftのMVVMの例が見つかりました。

これは正しいです?

コードは次のとおりです。

public class RelayCommand : ICommand
{
    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    /// <summary>
    /// Creates a new command that can always execute.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<object> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Creates a new command.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    #endregion // Constructors

    #region ICommand Members

    [DebuggerStepThrough]
    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    #endregion // ICommand Members
}

ProviderNew.xaml.csに以下を実装しました。

private ICommand saveCommand;
    public ICommand SaveCommand
    {
        get
        {
            if (saveCommand == null)
            {
                saveCommand = new RelayCommand(p => DoSave(), p => CanSave() );
            }
            return saveCommand;
        }
    }

    private bool CanSave()
    {
        if (!string.IsNullOrEmpty(ProviderName))
        {
            ??? What goes here? 
        }


        return true;
    }
private bool DoSave()
    {
        // Save the information to DB
    }

正直なところ、「if(!string.IsNullOrEmpty(ProviderName))」のブロックに何をコーディングする必要があるのか​​わかりません。

また、DataContextにICommandコードを追加すると言ったので、ウィンドウを開くと[保存]が有効になっていて、すべてのフィールドに正しいデータが含まれていてもクリックしても何も起こらないため、正しい場所にあるかどうかはわかりません。

これがボタンのProviderNewxamlコードです

<Button x:Name="btnSave" Height="25" Width="100" Content="Save" HorizontalAlignment="Right" Margin="0,0,120,0"
            Grid.Row="2" Command="{Binding SaveCommand}" >
        <Button.Style>
            <Style TargetType="{x:Type Button}">
                <Setter Property="IsEnabled" Value="False" />
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding ElementName=txtBoxHelpDesk, Path=(Validation.HasError)}" Value="False" />
                            <Condition Binding="{Binding ElementName=txtBoxProviderName, Path=(Validation.HasError)}" Value="False" />
                            <Condition Binding="{Binding ElementName=txtBoxRechargeInstructions, Path=(Validation.HasError)}" Value="False" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="IsEnabled" Value="True" />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>     
    </Button>

あなたの助けは非常にありがたいです。

よろしく、

ニール

4

1 に答える 1

2

わかりました、なんとかこの問題を再現できましたが、機能していないことに悩まされていました。しかし、私はそれを理解しました。
問題は、ValidationRule によって実際のプロパティ (ProviderName) が変更されないため、エラーが変更されたときにコンバーターが起動されないことです。Converter、ValidationRule、および Property-setter にブレークポイントを配置すると、この動作を確認できます。

したがって、 を使用する代わりにIValueConverter、 のバインディングをtbNameValidation次のように変更する必要があります。

<TextBlock x:Name="tbNameValidation"  
           Text="{Binding ElementName=txtBoxProviderName, 
                  Path=(Validation.Errors).CurrentItem.ErrorContent}">

重要な部分はPath=(Validation.Errors).CurrentItem.ErrorContent"。これにより、現在のエラー メッセージが表示されます。

また、「すべてのフィールドに値を入力してください。」というメッセージを最初から表示したい場合は、次Mode=TwoWayのバインディングに追加する必要がありtxtBoxProviderNameます。

 <Binding ElementName="This" 
          Path="ProviderName" 
          UpdateSourceTrigger="PropertyChanged"
          Mode="TwoWay">

パート2の編集

ボタンの有効なプロパティを修正するには、質問と同じコードを使用できますが、Clickイベントを使用して保存コードを実行する代わりに、Command代わりにプロパティ を使用できます。

DataContext で、 type のプロパティを作成しICommand、ボタンをこれにバインドします。

<Button Command="{Binding SaveCommand}" .... />

private ICommand saveCommand; 
public ICommand SaveCommand { 
    get { 
        if (saveCommand == null) {
            saveCommand = new RelayCommand(p => DoSave(), p=> CanSave() ); 
         } 
         return saveCommand; 
      } 
 }

CanSave 実装は、すべての期待が満たされているかどうかを確認できます。

private bool CanSave(){
   if (!string.IsNullOrEmpty(ProviderName)){
       //.... etc
   }
 }

CommandBinding は、バインドされたコマンドのメソッドCanSave()に従ってボタンの有効化を自動的に処理するため、 はボタンの状態を決定します。CanExecuteただし、バリデーターのロジックはここに戻ってくるので、そのコードを再利用する方法を見つけることをお勧めします。

コマンドの中継に関する詳細情報

于 2013-02-21T09:41:43.533 に答える