検証ルールに関するいくつかの投稿を調べましたが、発生している問題に遭遇していません。
WPFウィンドウのテキストボックスに検証ルールを使用しています。2つのチェックがあります。1つは空のテキストボックス用で、もう1つは正規表現の一致を使用した無効な文字用です。
私の問題はそのようなものです:
私のテキストボックス:
- タイプA-動作し、何も表示しない
- 空の文字列のバックスペースを押す-動作し、検証エラーメッセージ「すべてのフィールドに値を入力してください」を表示します
- タイプ !-動作しません-「無効な文字が見つかりました」と表示されますが、「すべてのフィールドに値を入力してください」と表示されます。
- 空の文字列へのバックスペース-「すべてのフィールドに値を入力してください」という最初のエラーが引き続き表示されるため、技術的には機能します。
- タイプA-動作します。エラーメッセージはありません
- タイプ !-問題ありません。「無効な文字が見つかりました。
同じことが逆に起こります。
窓を開ける
- タイプ !-ファイン、「無効な文字が見つかりました。
- 空の文字列へのバックスペース-「すべてのフィールドに値を入力してください」ではなく、「無効な文字が見つかりました」と表示されます。
私のコードは次のとおりです。
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>
あなたの助けは非常にありがたいです。
よろしく、
ニール