スポンサーサイト

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

Enterキー押下で(DataBindingの)ソースを更新しよう。

DataBindingのUpdateSourceTriggerには、

LostFocus(フォーカスが外れたら)
PropertyChanged(プロパティ値が変わったら)
Explicit(明示的にメソッドを呼ばないと)
Default(各プロパティの既定値)

がある。けれども、日本で比較的良く使われる、Enterキー押下でっていうのがない。PropertyChangedは、反応が良すぎる。入力してる最中でもどんどん反応する。Enterキー押下のみに反応しないものか?と調べた結果、良い記事を見つけた。
WPF TextBox DataBind on EnterKey press
なんとそのまんまだった。
添付プロパティを定義する。

using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// FrameworkElementビヘイビアクラス
/// </summary>
public static class FrameworkElementBehavior {

#region UpdatePropertySourceWhenEnterPressedプロパティ

/// <summary>
/// Enterキー押下でソースを更新する依存関係プロパティを取得します。
/// </summary>
/// <param name="obj">設定対象</param>
/// <returns>依存関係プロパティ</returns>
public static DependencyProperty GetUpdatePropertySourceWhenEnterPressed(DependencyObject obj) {
return (DependencyProperty)obj.GetValue(UpdatePropertySourceWhenEnterPressedProperty);
}

/// <summary>
/// Enterキー押下でソースを更新する依存関係プロパティを設定します。
/// </summary>
/// <param name="obj">設定対象</param>
/// <param name="value">依存関係プロパティ</param>
public static void SetUpdatePropertySourceWhenEnterPressed(DependencyObject obj, DependencyProperty value) {
obj.SetValue(UpdatePropertySourceWhenEnterPressedProperty, value);
}

/// <summary>Enterキー押下でソースを更新する依存関係プロパティ</summary>
public static readonly DependencyProperty UpdatePropertySourceWhenEnterPressedProperty =
DependencyProperty.RegisterAttached(
"UpdatePropertySourceWhenEnterPressed",
typeof(DependencyProperty), typeof(FrameworkElementBehavior),
new UIPropertyMetadata(null,
OnUpdatePropertySourceWhenEnterPressedPropertyChanged));
}
}
この添付プロパティに設定する値は、「依存関係プロパティ」になる。
変更検知用メソッドを実装する。

/// <summary>
/// UpdatePropertySourceWhenEnterPressedプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="dpObj">設定元</param>
/// <param name="e">イベント引数</param>
private static void OnUpdatePropertySourceWhenEnterPressedPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {
var element = dpObj as FrameworkElement;

if (element == null) {
//FrameworkElement以外は認めない
return;
}

element.PreviewKeyDown -= new KeyEventHandler(Element_PreviewKeyDown);

if (e.NewValue != null) {
//キーダウン確定前イベント
element.PreviewKeyDown += new KeyEventHandler(Element_PreviewKeyDown);
}

return;
}
デフォルト値(null)から設定された値に変更された事を検知した時に、PreviewKeyDownイベントをフックする為にハンドラ用メソッドを登録する。次に、PreviewKeyDownイベント用のメソッドを実装する。

/// <summary>
/// キーダウン確定前イベントハンドラ
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void Element_PreviewKeyDown(object sender, KeyEventArgs e) {

var element = sender as FrameworkElement;

if (e.Key == Key.Enter || e.Key == Key.Return) {
var targetProperty = FrameworkElementBehavior.
GetUpdatePropertySourceWhenEnterPressed(
e.Source as DependencyObject);
if (targetProperty != null) {
//対象とその対象が持つ依存関係プロパティに関する
//データバインディング情報を取得します。
var bindingExpression = BindingOperations.
GetBindingExpression(element, targetProperty);
if (bindingExpression != null) {
//ソースを更新だ
bindingExpression.UpdateSource();
}
}
}

return;
}
BindingOperationsクラスのGetBindingExpressionで、データバインディングの状態を取得できる。これがnullであれば、そもそも、その依存関係プロパティにデータバインディングが使われていないことを指している。例えば、

<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="213" Width="385"
>
<StackPanel>
<TextBox Text="テキストだ"/>
</StackPanel>
</Window>
みたいな画面を作ったとして、TextBoxのTextプロパティには、文字列が直接埋め込まれているので、今回の添付プロパティを使っても意味は無い。以下のようにデータバインディングされているプロパティにこそ、意味がある。

<Window x:Class="TawamureDays.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:nfc="http://nichia.jp/sys/foundation/component/gui"
xmlns:local="clr-namespace:TawamureDays"
Title="MainWindow" Height="213" Width="385"
>
<StackPanel>
<TextBox Text="{Binding InputText}"
local:FrameworkElementBehavior.
UpdatePropertySourceWhenEnterPressed="TextBox.Text"
/>
</StackPanel>
</Window>
添付プロパティに設定している「TextBox.Text」は、TextBox.TextPropertyを設定しているのと同じ(Propertyを省略できる)。流れはこんな感じ

Enterキーが押された
→押されたTextBoxについて、設定されたTextPropertyがデータバインディングされていないか、情報を取得する。
→バインディングされているなら、ソース(が持つであろう、InputTextプロパティの値)を更新する。

添付プロパティの便利さはこういう時にありがたく感じる。
(追記)
ただし、画面終了時に、PreviewKeyDownイベントからメソッドを削除できていないと、メモリリークの元となる(このstaticメソッドが、延々と参照し続ける)。
特にこういう、1画面に多数配置できるようなコントロールに設定する時は要注意だ。
リーク対策の為に、OnUpdatePropertySourceWhenEnterPressedPropertyChangedメソッドを変更する。

if (e.NewValue != null) {
//キーダウン確定前イベント
element.PreviewKeyDown += new KeyEventHandler(Element_PreviewKeyDown);
//画面終了時のイベント用メソッド
Action<object, EventArgs> closedHandler = null;
closedHandler = (sender1, e1) => {
var wnd = sender1 as Window;

if (closedHandler != null) {
wnd.Closed -= new EventHandler(closedHandler);
closedHandler = null;
}

element.PreviewKeyDown -=
new KeyEventHandler(Element_PreviewKeyDown);
//添付プロパティとのデータバインディングをクリアします。
BindingOperations.ClearBinding(element,
FrameworkElementBehavior.UpdatePropertySourceWhenEnterPressedProperty);
FrameworkElementBehavior.
SetUpdatePropertySourceWhenEnterPressed(element, null);
element = null;
return;
};

//Loaded時のイベント用メソッド
Action<object, RoutedEventArgs> loadedHandler = null;
loadedHandler = (sender, re) => {
var elem = sender as FrameworkElement;

if (loadedHandler != null) {
elem.Loaded -= new RoutedEventHandler(loadedHandler);
loadedHandler = null;
}
//このメソッドでelemを所有するウィンドウを取得できます。
var window = Window.GetWindow(elem);

window.Closed += new EventHandler(closedHandler);
return;
};

//Loadされていても実行するようにします。
if (!element.IsLoaded) {
element.Loaded += new RoutedEventHandler(loadedHandler);
} else {
loadedHandler(element, new RoutedEventArgs());
}
}
ラムダ式にしているのは、Closedイベントまで来ると、どのオブジェクトについて終了処理をして良いのかわからなくなるため。ラムダ式の外で生成されたインスタンスをラムダ式内で参照しても、結局はリークの元になるので、使い終わったらnullに初期化している。
これでもリークしないかというと、実際にチェックしてみるしかないかな。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


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

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

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

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

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