トップ回答者
入力エラーが起きた時、フォーカスを移動させなくするには

質問
回答
-
PreviewLostKeyboardFocusイベントで、現在のコントロールに検証エラーがある場合にはフォーカス移動をキャンセルしてみる。
#Livetは使ってないから、Livetの流儀にあってるかはわかりません。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525" PreviewLostKeyboardFocus="Window_PreviewLostKeyboardFocus" > <Window.BindingGroup> <BindingGroup Name="check" /> </Window.BindingGroup> <Grid> <StackPanel> <TextBox Text="バインディングしてないTextBox"/> <TextBox > <TextBox.Text> <Binding Path="NumberString" ValidatesOnDataErrors="true" /> <!-- IDataErrorInfoを使ってVM層で検証 --> </TextBox.Text> </TextBox> <TextBox> <TextBox.Text> <Binding Path="Data.Number" ValidatesOnDataErrors="true"> <Binding.ValidationRules> <app:NumberValidationRule ValidationStep="RawProposedValue"/><!-- 変換する前に検証する --> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> </StackPanel> </Grid> </Window>
namespace WpfApplication1 { public partial class MainWindow : System.Windows.Window { public MainWindow() { InitializeComponent(); this.DataContext = new VM(); } private void Window_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e) {//キーボードフォーカスが変更される前のイベント //BindingGroupに属しているBindingを強制的に検証させる。 this.BindingGroup.CommitEdit(); System.Windows.DependencyObject dp = e.OldFocus as System.Windows.DependencyObject; if (dp != null && System.Windows.Controls.Validation.GetHasError(dp)) { //フォーカスを持っているコントロールが検証エラーの場合にはメッセージを表示。 System.Windows.MessageBox.Show(System.Windows.Controls.Validation.GetErrors(dp)[0].ErrorContent.ToString()); //フォーカス移動をキャンセルさせる。 e.Handled = true; } } } //ここから下はVMとか作っているだけ class VMBase : System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyPropertyChanged { #region IDataErrorInfo メンバー private System.Collections.Generic.Dictionary<string, string> errors = new System.Collections.Generic.Dictionary<string, string>(); public string Error { get { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (string s in errors.Values) { sb.AppendLine(s); } return sb.ToString(); } } public string this[string columnName] { get { return errors[columnName]; } } protected void SetError(string column, string err) { this.errors[column] = err; } protected void ClearError(string column) { this.errors.Remove(column); } #endregion #region INotifyPropertyChanged メンバー public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { var pc = PropertyChanged; if (pc != null) { pc(this, new System.ComponentModel.PropertyChangedEventArgs(name)); } } #endregion } class VM : VMBase { public VM() { this.Data = new Data(); } public Data Data { get; private set; } public string NumberString { get { return Data.Number.ToString(); } set { double d; if (double.TryParse(value, out d)) { this.Data.Number = d; base.ClearError("NumberString"); } else { base.SetError("NumberString", "IDataErrorInfo 数値を入力してください"); } OnPropertyChanged("NumberString"); } } } class Data { public double Number { get; set; } } class NumberValidationRule : System.Windows.Controls.ValidationRule { public override System.Windows.Controls.ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { bool canConvert = false; if (value == null) { return new System.Windows.Controls.ValidationResult(false, "入力してください"); } System.IConvertible ic = value as System.IConvertible; if (ic != null) { try { double d = ic.ToDouble(cultureInfo); canConvert = true; } catch { } } else { double d; canConvert = double.TryParse(value.ToString(), out d); } if (!canConvert) { return new System.Windows.Controls.ValidationResult(false, "ValidationRule 数値に変換できません"); } else { return new System.Windows.Controls.ValidationResult(true, null); } } } }
VB.NetかC#かわからなかったのでC#で書いてますが、VB.NetでもPreviewLostKeyboardFocusのところを追加すればわかると思います。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
すべての返信
-
PreviewLostKeyboardFocusイベントで、現在のコントロールに検証エラーがある場合にはフォーカス移動をキャンセルしてみる。
#Livetは使ってないから、Livetの流儀にあってるかはわかりません。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:app="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525" PreviewLostKeyboardFocus="Window_PreviewLostKeyboardFocus" > <Window.BindingGroup> <BindingGroup Name="check" /> </Window.BindingGroup> <Grid> <StackPanel> <TextBox Text="バインディングしてないTextBox"/> <TextBox > <TextBox.Text> <Binding Path="NumberString" ValidatesOnDataErrors="true" /> <!-- IDataErrorInfoを使ってVM層で検証 --> </TextBox.Text> </TextBox> <TextBox> <TextBox.Text> <Binding Path="Data.Number" ValidatesOnDataErrors="true"> <Binding.ValidationRules> <app:NumberValidationRule ValidationStep="RawProposedValue"/><!-- 変換する前に検証する --> </Binding.ValidationRules> </Binding> </TextBox.Text> </TextBox> </StackPanel> </Grid> </Window>
namespace WpfApplication1 { public partial class MainWindow : System.Windows.Window { public MainWindow() { InitializeComponent(); this.DataContext = new VM(); } private void Window_PreviewLostKeyboardFocus(object sender, System.Windows.Input.KeyboardFocusChangedEventArgs e) {//キーボードフォーカスが変更される前のイベント //BindingGroupに属しているBindingを強制的に検証させる。 this.BindingGroup.CommitEdit(); System.Windows.DependencyObject dp = e.OldFocus as System.Windows.DependencyObject; if (dp != null && System.Windows.Controls.Validation.GetHasError(dp)) { //フォーカスを持っているコントロールが検証エラーの場合にはメッセージを表示。 System.Windows.MessageBox.Show(System.Windows.Controls.Validation.GetErrors(dp)[0].ErrorContent.ToString()); //フォーカス移動をキャンセルさせる。 e.Handled = true; } } } //ここから下はVMとか作っているだけ class VMBase : System.ComponentModel.IDataErrorInfo, System.ComponentModel.INotifyPropertyChanged { #region IDataErrorInfo メンバー private System.Collections.Generic.Dictionary<string, string> errors = new System.Collections.Generic.Dictionary<string, string>(); public string Error { get { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (string s in errors.Values) { sb.AppendLine(s); } return sb.ToString(); } } public string this[string columnName] { get { return errors[columnName]; } } protected void SetError(string column, string err) { this.errors[column] = err; } protected void ClearError(string column) { this.errors.Remove(column); } #endregion #region INotifyPropertyChanged メンバー public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string name) { var pc = PropertyChanged; if (pc != null) { pc(this, new System.ComponentModel.PropertyChangedEventArgs(name)); } } #endregion } class VM : VMBase { public VM() { this.Data = new Data(); } public Data Data { get; private set; } public string NumberString { get { return Data.Number.ToString(); } set { double d; if (double.TryParse(value, out d)) { this.Data.Number = d; base.ClearError("NumberString"); } else { base.SetError("NumberString", "IDataErrorInfo 数値を入力してください"); } OnPropertyChanged("NumberString"); } } } class Data { public double Number { get; set; } } class NumberValidationRule : System.Windows.Controls.ValidationRule { public override System.Windows.Controls.ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo) { bool canConvert = false; if (value == null) { return new System.Windows.Controls.ValidationResult(false, "入力してください"); } System.IConvertible ic = value as System.IConvertible; if (ic != null) { try { double d = ic.ToDouble(cultureInfo); canConvert = true; } catch { } } else { double d; canConvert = double.TryParse(value.ToString(), out d); } if (!canConvert) { return new System.Windows.Controls.ValidationResult(false, "ValidationRule 数値に変換できません"); } else { return new System.Windows.Controls.ValidationResult(true, null); } } } }
VB.NetかC#かわからなかったのでC#で書いてますが、VB.NetでもPreviewLostKeyboardFocusのところを追加すればわかると思います。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
-
私も回答ではないのですが、このような実装をあえて必要があって行うのであれば良いのですが、どうしてもこのようにする理由が無ければ、他の実装を検討されることをお勧めします。
もし、テキストボックスに限らず、入力する項目がたくさんある場合、一つずつこのような実装をするのは煩雑ですし、ユーザーにとっても煩わしく感じる場合があるように思います。
IDataErrorInfoを使っているのであれば、エラーの場合、デフォルトでテキストボックスの周りが赤くなるはずですので、それに加えてToolTipでエラー内容を表示し、エラーがあれば例えば登録ボタンを不活性にする実装などが、代替として考えられます。★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/