スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: スポンサー広告

TextBoxのLostFocusイベントが肝心な時に発生しない。

 WPFで画面を開発していた時の話。
 マスタデータ等をメンテナンスするような画面では、画面終了時に、内容を変更していた時に限って終了を確認する為のメッセージを出す事がある。これは変更途中で間違って「x」ボタンを押してしまい、全部おじゃんにならなくて済むようにする為の保険になったりする。
 そんな感じで動く画面を作り、他の人にテストしてもらった時、NGがついてしまった。チェックした人が言うには、データを変更して画面を終了しようとしても、メッセージも出ずそのまま終了してしまうとの事だった。
 その画面では、TextBoxのTextプロパティとViewModel側のプロパティとは、下のような感じでデータバインディングしている。
<TextBox Text="{Binding InputText, UpdateSourceTrigger=LostFocus}"/>
TextBoxからフォーカスが外れた時点で、バインディングソースに変更通知が伝播するようにしていた。しかし、どうもLostFocusイベントが発生しないようだった。
しっかり再現もした。

(1)TextBoxにフォーカスしてカーソルが出ている状態にする。
(2)任意の文字を入力し、そのままマウスで右上の「x」ボタンを押す。
(3)そのまま終わってしまう。

WPFの仕様なのか、画面終了イベント(Closing)の後にLostFocusイベントが発生しているようだった。逆じゃねーかと半ギレしてみても、事態は何も変わらない。一番手っ取り早いのは、UpdateSourceTriggerをPropertyChangedに変更することだ。
<TextBox Text="{Binding InputText, UpdateSourceTrigger=PropertyChanged}"/>
うん。これで良い筈なんだけど…。これはこれでイベント通知が激しすぎるのが気になるんだ。何を入力しても即座に反応するので、更新通知頻度がとても高い。そのうえ、TextBoxからフォーカスが外れた時にのみチェックすれば良い事項があった時は、そもそもPropertyChangedにできない。結局、困った時のCommandさんに頼るのであった。
○View側のContentControlを継承したクラス内でコマンドを宣言する。
/// <summary>
/// フォーカス中コントロール バインディングソース更新コマンドを取得|設定します。
/// </summary>
public ICommand UpdateFocucedElementCommand {
get { return (ICommand)GetValue(UpdateFocucedElementCommandProperty); }
set { SetValue(UpdateFocucedElementCommandProperty, value); }
}

/// <summary>フォーカス中コントロール バインディングソース更新コマンド</summary>
public static readonly DependencyProperty UpdateFocucedElementCommandProperty =
DependencyProperty.Register("UpdateFocucedElementCommand",
typeof(ICommand),
typeof(TawamureContents),
new UIPropertyMetadata(null));
○実行用メソッドを実装する。
/// <summary>
/// UpdateFocucedElementコマンド実行メソッド<br/>
/// フォーカス中のコントロールのバインディングソースを更新します。<br/>
/// </summary>
/// <param name="parameter">コマンドパラメータ</param>
private void ExecuteUpdateFocucedElement(object parameter) {

var focuedElement = Keyboard.FocusedElement;

if (focuedElement == null) {
return;
}

BindingExpression bindingExpr = null;

if (focuedElement is TextBox) {
bindingExpr = BindingOperations.GetBindingExpression(
focuedElement as TextBox, TextBox.TextProperty);

} else if (focuedElement is DatePicker) {
bindingExpr = BindingOperations.GetBindingExpression(
focuedElement as DatePicker, DatePicker.SelectedDateProperty);

} else if (focuedElement is ComboBox) {
bindingExpr = BindingOperations.GetBindingExpression(
focuedElement as ComboBox, ComboBox.SelectedValueProperty);

} else if (focuedElement is ListBox) {
bindingExpr = BindingOperations.GetBindingExpression(
focuedElement as ListBox, ListBox.SelectedValueProperty);
}

if (bindingExpr != null && !bindingExpr.HasError) {
bindingExpr.UpdateSource();
}

return;
}
とりあえずTextBoxをカバーできればそれで良い。後はおまけ。
○インスタンスを生成して、プロパティに設定する。
/// <summary>
/// (依存関係)プロパティ値変更イベントハンドラ
/// </summary>
/// <param name="e">イベントデータ</param>
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e) {
base.OnPropertyChanged(e);

if (e.Property.Name == DataContextProperty.Name) {
//DataContextがセットされたら、コマンドのインスタンスを生成します。
//正しくバインディングされてれば、ViewModelからコマンドを実行できる筈。
if (e.NewValue != null) {
this.UpdateFocucedElementCommand =
new RelayCommand(ExecuteUpdateFocucedElement);
}
}

return;
}
○ViewModel側でもプロパティを宣言する。
/// <summary>
/// フォーカス中要素のバインディングソースを更新するコマンドを取得|設定します。
/// </summary>
public ICommand UpdateFocucedElementCommand {
get; set;
}
同じ名前にしておけば、間違わない。これは好みの問題。
○View側でバインディング
<tw:TawamureContents UpdateFocucedElementCommand="{Binding UpdateFocucedElementCommand, Mode=OneWayToSource}">
...
</tw:TawamureContents>

Target(View)側からSource(ViewModel)に流すので、ModeはOneWayToSourceとなる。
更に、ViewModel側にコマンドをラップするメソッドを作れば、nullチェックは不要になる。
/// <summary>
/// フォーカス中要素のバインディングソースを更新します。
/// </summary>
protected void UpdateFocucedElementSource() {
if (this.UpdateFocucedElementCommand != null) {
this.UpdateFocucedElementCommand.Execute(null);
}
}
このメソッドをボタンを押した時に発生するイベントやコマンド用メソッド、或いは画面終了時用のメソッド内で呼び出してやれば、ソース側に値が送られてちゃんと処理できることになる。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


サイドバー背後固定表示サンプル

当ブログに書かれたソースコードは流用自由です。

バグ、スペルミス等はありうる事です。

ご利用の際は自己責任でお願いしますm(_ _)m

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。