数値だけのTextBoxだってやれるはずだ(参)

・AllowOnlyNumeric用Workerクラスを実装する。
・TextCompositionManagerクラスのPreviewTextInputイベントへハンドラを登録する。
/// <summary>
/// AllowOnlyNumericProperty用Workerクラス
/// </summary>
internal class AllowOnlyNumericPropertyWorker : AttachedPropertyWorker {

#region AttachedPropertyWorkerメンバー

/// <summary>
/// 初期化処理を行います。
/// </summary>
protected override void OnWorkerSet() {
var textBox = this.AttachedElement as TextBox;
TextCompositionManager.AddPreviewTextInputHandler(textBox,
AllowOnlyNumericPropertyWorker.OnPreviewTextInput);
return;
}

/// <summary>
/// 内部リソースを解放します。
/// </summary>
/// <param name="disposing">false:アンマネージリソースのみ解放します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

if (disposing) {
var textBox = this.AttachedElement as TextBox;
TextCompositionManager.RemovePreviewTextInputHandler(textBox,
AllowOnlyNumericPropertyWorker.OnPreviewTextInput);
}
}

#endregion

#region イベント用メソッド

/// <summary>
/// TextComposition完了イベント用ハンドラ<br/>
/// 入力確定時のTextChangedイベントの前に発生します。<br/>
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e) {
var textBox = sender as TextBox;

return;
}

#endregion
}
終了処理(OnDispose)は必須である。登録と共に実装すれば、メモリリークが少なくなる。
これを添付プロパティの値変更時に登録するように実装する。
/// <summary>
/// AllowOnlyNumericプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="dpObj">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnAllowOnlyNumericPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {

var textBox = dpObj as TextBox;

if (textBox == null) {
return;
}

if ((bool)e.NewValue) {
//TextBoxPastingWorkerは、貼り付け処理専用のWorkerクラス
//同時使用が想定されるプロパティで使いまわす
AttachedPropertyWorker.SetWorker<TextBoxPastingWorker>(
textBox,
TextBoxBehavior.PastingTextWorkerProperty);
AttachedPropertyWorker.SetWorker<AllowOnlyNumericPropertyWorker>(
textBox,
TextBoxBehavior.AllowOnlyNumericWorkerProperty);

} else {
textBox.SetValue(TextBoxBehavior.PastingTextWorkerProperty, null);
textBox.SetValue(TextBoxBehavior.AllowOnlyNumericWorkerProperty, null);
}

return;
}

なお、バイト長で制限したいときもある(四)。で実装した貼り付け(Paste)の実装を、別のWorkerクラス(TextBoxPastingWorker)にして、LengthAsByteプロパティと同時に使用できるようにしている。

OnPreviewTextInputメソッド実装メモ

・入力されたテキストを半角に変換する。
 日本語入力がONかOFFかで扱いも代わるので注意。
・マイナス(-)が入力されたら、前につける。既についていれば、外す(符号の反転)。
・選択されているTextがあれば、それと入れ替える、という処理を忘れずに。
・入力されたのが数値だとしても、追加の制限を満たさなければ、入力をキャンセルする形にする。
 整数部桁数
 小数部桁数
 最大値
 最小値
・上記追加制限のチェックは、貼り付け時にもチェックする必要があるので、メソッド化をしておく。

続く
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

数値だけのTextBoxだってやれるはずだ(弐)

添付プロパティを用意する。
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;

namespace TawamureDays {

/// <summary>
/// Description of TextBoxBehavior.
/// </summary>
public static class TextBoxBehavior {

#region AllowOnlyNumericプロパティ

/// <summary>
/// 数値入力のみ許可するかどうかを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>true:数値入力のみ許可する</returns>
public static bool GetAllowOnlyNumeric(DependencyObject obj) {
return (bool)obj.GetValue(AllowOnlyNumericProperty);
}

/// <summary>
/// 数値入力のみ許可するかどうかを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">true:数値入力のみ許可する</param>
public static void SetAllowOnlyNumeric(DependencyObject obj, bool value) {
obj.SetValue(AllowOnlyNumericProperty, value);
}

/// <summary>数値入力のみ許可するかどうか</summary>
public static readonly DependencyProperty AllowOnlyNumericProperty =
DependencyProperty.RegisterAttached("AllowOnlyNumeric", typeof(bool),
typeof(TextBoxBehavior),
new UIPropertyMetadata(false,
OnAllowOnlyNumericPropertyChanged));

/// <summary>
/// AllowOnlyNumeric用Workerを取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>AllowOnlyNumeric用Worker</returns>
public static AttachedPropertyWorker GetAllowOnlyNumericWorker(DependencyObject obj) {
return (AttachedPropertyWorker)obj.GetValue(AllowOnlyNumericWorkerProperty);
}

/// <summary>
/// AllowOnlyNumeric用Workerを設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">AllowOnlyNumeric用Worker</param>
public static void SetAllowOnlyNumericWorker(DependencyObject obj, AttachedPropertyWorker value) {
obj.SetValue(AllowOnlyNumericWorkerProperty, value);
}

/// <summary>AllowOnlyNumeric用Worker</summary>
public static readonly DependencyProperty AllowOnlyNumericWorkerProperty =
DependencyProperty.RegisterAttached("AllowOnlyNumericWorker",
typeof(AttachedPropertyWorker),
typeof(TextBoxBehavior),
new UIPropertyMetadata());

/// <summary>
/// AllowOnlyNumericプロパティ値変更イベントハンドラ
/// </summary>
/// <param name="dpObj">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnAllowOnlyNumericPropertyChanged(
DependencyObject dpObj, DependencyPropertyChangedEventArgs e) {

var textBox = dpObj as TextBox;

if (textBox == null) {
return;
}

if ((bool)e.NewValue) {
//
} else {
//
}

return;
}

/// <summary>
/// 整数部桁数を取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>整数部桁数</returns>
public static int GetIntegerPartDigit(DependencyObject obj) {
return (int)obj.GetValue(IntegerPartDigitProperty);
}

/// <summary>
/// 整数部桁数を設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">整数部桁数</param>
public static void SetIntegerPartDigit(DependencyObject obj, int value) {
obj.SetValue(IntegerPartDigitProperty, value);
}

/// <summary>整数部桁数</summary>
public static readonly DependencyProperty IntegerPartDigitProperty =
DependencyProperty.RegisterAttached("IntegerPartDigit", typeof(int),
typeof(TextBoxBehavior),
new UIPropertyMetadata(0));

/// <summary>
/// 小数部桁数を取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>小数部桁数</returns>
public static int GetDecimalPartDigit(DependencyObject obj) {
return (int)obj.GetValue(DecimalPartDigitProperty);
}

/// <summary>
/// 小数部桁数を設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">小数部桁数</param>
public static void SetDecimalPartDigit(DependencyObject obj, int value) {
obj.SetValue(DecimalPartDigitProperty, value);
}

/// <summary>小数部桁数</summary>
public static readonly DependencyProperty DecimalPartDigitProperty =
DependencyProperty.RegisterAttached("DecimalPartDigit", typeof(int),
typeof(TextBoxBehavior),
new UIPropertyMetadata(0));

/// <summary>
/// 最小値を取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>最小値</returns>
public static decimal? GetMinValue(DependencyObject obj) {
return (decimal?)obj.GetValue(MinValueProperty);
}

/// <summary>
/// 最小値を設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">最小値</param>
public static void SetMinValue(DependencyObject obj, decimal? value) {
obj.SetValue(MinValueProperty, value);
}

/// <summary>最小値</summary>
public static readonly DependencyProperty MinValueProperty =
DependencyProperty.RegisterAttached("MinValue", typeof(decimal?),
typeof(TextBoxBehavior),
new UIPropertyMetadata(null));

/// <summary>
/// 最大値を取得します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <returns>最大値</returns>
public static decimal? GetMaxValue(DependencyObject obj) {
return (decimal?)obj.GetValue(MaxValueProperty);
}

/// <summary>
/// 最大値を設定します。
/// </summary>
/// <param name="obj">対象オブジェクト(TextBox)</param>
/// <param name="value">最大値</param>
public static void SetMaxValue(DependencyObject obj, decimal? value) {
obj.SetValue(MaxValueProperty, value);
}

/// <summary>最大値</summary>
public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.RegisterAttached("MaxValue", typeof(decimal?),
typeof(TextBoxBehavior),
new UIPropertyMetadata(null));

#endregion
}
}

次にWorkerクラスを実装する。つづく。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

IIS7、VS2010起動中、作ったWebサイトでエラー

VS2010を使いながら、IIS7にアプリ(Webサイト)をたちあげてみたら、ページにアクセスできなかった。
エラー内容でぐぐってみると、こういう記事が出てきた。
IIS 7.xに配置すると「HTTP エラー 500.23 - Internal Server Error ASP.NET 設定が、統合されたマネージ パイプライン モードで適用されないことが検出されました。」が発生します。
対処方法があったので、そのとおりにしたら、大丈夫だった。こういうよくわからない類のエラーって、本に載ってないからな。便利になったもんだ。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WEB | コメント: 0 | トラックバック: 0

数値だけのTextBoxだってやれるはずだ(壱)。

数値入力オンリーのテキストボックスだって作れるんだぞ。

数値型プロパティとTextBox.Textプロパティとのバインド

数値入力オンリーにしなくたって方法はある。かな。
を書いた時は、TextCompositionManagerの存在を知らなかった。でも今は知っている。バイト長で制限した時の実装を応用して、そういうのもできる筈!という事で、数値専用のTextBoxってやつを実装してみた。実際に仕事では活用している。

○直接入力について
 これはTextCompositionManagerクラスのPreviewTextInputイベントでどうにかなる。
○日本語入力のON/OFFについて
 PreviewKeyDownイベントで、日本語入力をONにした状態のキーデータ(e.Key)はすべて「ImeProcess」になっているので、これに相当するときは以後の処理をキャンセルすることでどうにかなりそう。
○PreviewKeyDownイベントでフックできないアクションを各々実装する。
 ・貼り付け
  貼り付けられる文字列に数値が含まれてあれば、それを抽出する、あるいは数値(decimal)に変換できなければ、無視をする。これは運用次第かな。
 ・削除(Delete)
  キャレットの右の文字を削除する。選択されている文字列があれば、それをまるごと消す。
  Ctrl+Delの動きをどうしよう?
 ・BS(Backspace)
  キャレットの左の文字を削除する。選択されている文字列があれば、それをまるごと消す。
  Ctrl+BSの動きをどうしよう?

○主に用意する添付プロパティ
 ・数値入力のみにするかどうか:
  これがメイン。外せば元に戻るのがミソ。というか、WPFが持つ強みの一つ。コントロールを継承しないで済むってのが大きい。
 ・整数部の桁数、小数部の桁数
  データベース内のテーブルで小数点もちの値はほぼdecimal型に放り込む。その際、整数部と小数部の桁数は結構揉めるネタとなる。やれ単価の小数点は何桁までとか、何桁目は四捨五入するのか、切り捨てるのかとか。
 ・最大値と最小値:
  桁数だけでは制御できないところを決めるプロパティ。4桁だけど最大値は5000だよ、とか。
つづく。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

結構前からあったんだねOrz

DataGridに対する添付プロパティの実装で、半角/全角を区別しないで文字列を扱えないものかとしらべてみたら、結構前からあった事が判明した。
大文字小文字の区別無しの比較は、string.Compareがあるのは知ってたんだけど…。

大文字小文字、半角全角、ひらがなカタカナの区別をしないで文字列を比較する
CompareInfo クラス

どうも、全角/半角どころか、カナ(平仮名/片仮名)すら無視して比較できるらしい。
おお、これだ!って感じだったんだけど、もっと前から知っていればという気がしないでもない。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

バイト長で制限したいときもある(五)

バイト長で制限したいときもある(四)。で実装が終わりだと思ってたんだけど、まだ対応できていないケースを発見してしまった。それは、改行コード
WPFのTextBoxは、AcceptReturnというプロパティをTrueに設定すると、改行コードを入力できるようになる。しかし、この改行コードを入力しても、件のPreviewTextInputイベントは発生しない。貼り付けでもないので、コマンド処理ではどうにもならない。
しかし、改行コードも0バイトではないし、データベースにエントリーするときは、その規定文字数にきっちりとカウントされる。なのでバイト数制限をするなら、改行コードもカウントする必要がある。その改行コード対策として、PreviewKeyDownイベントを利用する事にした。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

指定のバイト数で文字列をカットする

バイト長で制限したいときもある(四)。でつかったCutByteLengthメソッドを書いてなかったので、載せておく。
using System;

namespace TawamureDays {

public static class Utils {

/// <summary>
/// 指定された文字列の指定バイト長より右の文字列を切り捨てます。<br/>
/// </summary>
/// <param name="str">元文字列</param>
/// <param name="length">バイト長(1以上を指定)</param>
/// <returns>切捨て後の文字列</returns>
public static string CutByteLength(string str, int length) {
if (Utils.GetByteLength(str) <= length) {
//指定したバイト数より短い場合のみ、そのまま返します。
return str;
}

if (length <= 0) {
return string.Empty;
}

// 文字の配列に分解して長さを確認します。
string buff = null;
var sb = new System.Text.StringBuilder();

foreach (char ch in ToString(str).ToCharArray()) {
buff = sb.ToString();
sb.Append(ch);

if (Utils.GetByteLength(sb.ToString()) > length) {
return buff;
}
}
return sb.ToString();
}
}
}
.NET1.1の頃から使っているので、なんともベタなロジックだな。
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

バイト長で制限したいときもある(四)。

バイト長で制限したいときもある(参)で、直接入力をバイト数で制限した。しかし、「貼り付け」というアクションでPreviewTextInputイベントは発生しない為、貼り付けに対する制限を実装する必要がある。
WPFにおいて、コピーや貼り付けというアクションは、予めコマンドとして用意されている。
[C#][WPF]コマンドですよ その8 「用意されてるコマンド」
ApplicationCommands Class
TextBoxに対する貼り付けアクションを独自に実装したい場合は、TextBoxのCommandBindingsに追加して、自分の実装で上書きする。
/// <summary>貼り付け用CommandBindingオブジェクト</summary>
private CommandBinding pasteCommandBinding_;

/// <summary>
/// 初期化処理を行います。
/// </summary>
protected override void OnWorkerSet() {

var textBox = this.AttachedElement as TextBox;
this.pasteCommandBinding_ =
new CommandBinding(ApplicationCommands.Paste,
new ExecutedRoutedEventHandler(OnExecutePasted),
new CanExecuteRoutedEventHandler(OnCanExecutePasted));

textBox.CommandBindings.Add(this.pasteCommandBinding_);
TextCompositionManager.AddPreviewTextInputHandler(textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);

return;
}
これに対して、後始末も追加する
/// <summary>
/// 内部リソースを解放します。
/// </summary>
/// <param name="disposing">false:アンマネージリソースのみ解放します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

if (disposing) {
var textBox = this.AttachedElement as TextBox;

if (this.pasteCommandBinding_ != null) {
textBox.CommandBindings.Remove(this.pasteCommandBinding_);
this.pasteCommandBinding_ = null;
}

TextCompositionManager.RemovePreviewTextInputHandler(textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);

}

base.OnDispose(disposing);
return;
}

CommandBindingsにやられました。にも書いたけど、Addメソッドで追加したものと同じインスタンスをRemoveメソッドに渡さないとちゃんと削除できない。
本命のメソッドは以下のような感じ続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

バイト長で制限したいときもある(参)。

バイト長制限においてやっかいなのが、日本語入力。日本語入力時、PreviewKeyDownのイベントデータにある「e.Key」の値がすべて「ImeProcess」になるため、それでは防げなかった。
なので、バイト長で制限したいときもある(壱)にも書いたように、TextCompositionManagerクラスを使うことにした。TextChangedイベントを使うタイプは、そのイベント内で下手にTextプロパティをいじると落ちる事があるので今回は採用しない。
namespace TawamureDays {

class LengthAsBytePropertyWorker : AttachedPropertyWorker {

/// <summary>
/// 初期化処理を行います。
/// </summary>
protected override void OnWorkerSet() {

var textBox = this.AttachedElement as TextBox;
TextCompositionManager.AddPreviewTextInputHandler(
textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);
return;
}

/// <summary>
/// 内部リソースを解放します。
/// </summary>
/// <param name="disposing">false:アンマネージリソースのみ解放します。</param>
protected override void OnDispose(bool disposing) {
if (this.IsDisposed) {
return;
}

//後始末
if (disposing) {
var textBox = this.AttachedElement as TextBox;
TextCompositionManager.RemovePreviewTextInputHandler(textBox,
LengthAsBytePropertyWorker.OnPreviewTextInput);
}
}

/// <summary>
/// TextComposition完了イベント用ハンドラ<br/>
/// 入力確定時のTextChangedイベントの前に発生します。<br/>
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnPreviewTextInput(object sender, TextCompositionEventArgs e) {
var textBox = sender as TextBox;

return;
}
}
}

○OnPreviewTextInputメソッド内の実装ポイント:
・入力中の文字列のバイト数と、現在入力済のテキストが制限バイト数を超えないようにする。
・選択中のテキストがあれば、それと入れ替わるので、バイト数の計算に注意する。
・設定後のカーソルの位置も自分で調整する。
・注意点:
 日本語入力のONとOFFによってイベントデータ(e)の内容が異なる。
 日本語入力ON:textBox.Text内に入力中の文字列が既に含まれている。
 日本語入力OFF:textBox.Text内に入力中の文字列は含まれていない。
 入力中の文字列は、イベントデータ(e)のTextプロパティ内にあるので、それを使う。なお、日本語入力の状態(ON/OFF)は、InputMethod.Current.ImeStateでチェックできる。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

バイト長で制限したいときもある(弐)。

バイト長で制限したいときもある(壱)。でやりたかった事を、いつか必ず後始末(弐)で作ったクラスを使って実装する。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

いつか必ず後始末(弐)

いつか必ず後始末(壱)の続き

添付プロパティの終了処理をいつか必ず行う為のクラスを作る事にした。なお、仕事で作ったものとはちょっと変えてみた。

やりたい事。
・Loadedイベント発生時に、WindowのClosedイベントへハンドラメソッドを登録する。
・Window.Closedイベントで必ず添付プロパティに関する終了処理を行う。
・終了処理とは、添付プロパティ値変更時に行った処理を打ち消す処理の事。
 例:値変更時にイベントへハンドラを登録→終了時にはイベントからハンドラを削除
・継承したクラスで、必要な処理も実装できるクラスになれれば良い。

クラスの名前は何にしようか。まあ、適当に「AttachedPropertyWorker」とでもつけるか。
以下、コードとその説明を載せておく(長い)。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

バイト長で制限したいときもある(壱)。

C#は、内部のコードや文字列をUnicodeで持っている。その関係で、stringクラスが持つLengthプロパティは、バイト長ではなく、文字数を返す仕様となっている。では、バイト長をどうやって取得するか?というと、Shift_JISに変換した上でLengthを取得する方法が一般的になる。
using System;
using System.Text;

namespace TawamureDays {

public class Utils {

/// <summary>
/// 指定された文字列のバイト数を返します。
/// </summary>
/// <param name="str">指定文字列</param>
/// <returns>バイト数</returns>
public static long GetByteLength(string str) {
if (Utils.IsEmpty(str)) {
return 0;
}

//Shift_JISに変更してバイト数をカウントする
//Unicodeはどんな文字でも2バイトだった...
return Encoding.GetEncoding("Shift_JIS").GetByteCount(str);
}
}
}

ところで、TextBoxにはMaxLengthというプロパティがあり、これも例外なく文字数の制限となる。これを使ったところで、バイト長による制限を掛ける事はできない。
そもそも、なぜ今の御時世にバイト長?かというと、その制限を要求される項目が帳票に出力されるからだ。
帳票はいつの時代も、ペーパーレスだのエコだのと言われながらも、紙で必要とされる種類がある。だって、ハッキングされないし。ちゃんと保管していれば、なくならないし。業者のミスにより消される事もないし。電子データを保管するHDDなんかの耐用年数なんかとは比べ物にならない年数を保管できるし。受注伝票、納品書、製品仕様書等々。いつの時代も帳票は必要とされる。逆にこれが出力されないと、電子化する意味あんの?という気がする業務もあったりするし。
で、開発中の画面も、そういう帳票に出る項目があり、バイト長による制限を付与する必要がある。

・一番良いのは、制限以上の文字を入力できなくすることだ。
 →でも、MaxLengthは文字数による制限なので、完全とはいえない。
・次に良いのは、そのバイト数を超えたら、エラーにすることだ。
 →数値入力オンリーにしなくたって方法はある。かな。でやったようなValidationRuleクラスを使う。
次善策を作ることはできた。ただ、同僚から、「やっぱ制限かけませんか?」という話になって、バイト長による入力制限を調べることになった。現在は、まだ完全に制限できていない為、現在までのメモを列挙しておく。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

いつか必ず後始末(壱)

Windows.Formsにかぎらず、WPFでも、イベント機能を使用する。専用の構文(event)で宣言されたイベントに対し、「+=」でリスナとなるメソッドを登録し、「-=」でリスナとなるメソッドを削除する。Javaの世界では有名なGOFのObserver パターンに似ているだろうか。
public class AAAA {
public event EventHandler TawamureDrived;
}

あるいは、
public class AAAA {
private EventHandler tawamureDrived_;

public event EventHandler TawamureDrived {
add {tawamureDrived_ += value;}
remove {tawamureDrived_ -= value;}
}
}

という感じで定義する。
WPFの依存関係プロパティという機能でも、このイベントとは切れない関係にあった。依存関係プロパティに設定した値が変化した時に、それをトリガーとして、イベントにメソッドを登録する。
MVVMパターンで、イベントをどうにかしよう。
でも書いた、Loadedイベントとコマンドを連携させるときなんかが典型となる。上の記事では特に書いていないが、いつか必ず後始末をする必要がある。前にそれをネタにちょっと書いた事がある。
添付プロパティの注意点
イベントへのメソッド登録は、そのメソッドを持つクラスのインスタンスを参照するという事と同じ意味になる。「そのインスタンスを参照するオブジェクトが消えない限り、ガベージコレクトされる事はない」ので、結局それが残り続け、メモリリークの元となる。
frwElement.Loaded += new RoutedEventHandler(FrwElement_Loaded);
としたのであれば、どこかで
frwElement.Loaded -= new RoutedEventHandler(FrwElement_Loaded);
とする必要がある。イベントを持つインスタンスとメソッドを持つインスタンスが事なる時は特にだ。タイミング的には、「ウィンドウが閉じられた時」だと思っているので、WindowクラスのClosedイベントにメソッドを登録してイベントを削除するようにしている。
/// <summary>
/// Loadedイベントハンドラ用メソッド
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベント引数</param>
private static void FrwElement_Loaded(object sender, RoutedEventArgs e) {
//Loadedイベントを持つのは、FrameworkElement継承くらすのみ。
var frwElement = sender as FrameworkElement;
var window = Window.GetWindow(element);//親ウィンドウを取得する。

if (window != null) {

Action<object, EventArgs> closedHandler = null;

closedHandler = (sender2, e2) => {
//こうしないと、Loadedイベントに登録したメソッドの削除ができない
frwElement.Loaded -=
new RoutedEventHandler(FrwElement_Loaded);

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

window.Closed += new EventHandler(closedHandler);
}
}
このやり方にも少々問題がある。
・添付プロパティ1つ1つに対して、Loadedイベントを登録する必要があるので、メソッドが重複しないように苦心する必要がある(主にメソッド名)。
・Loadedイベントからのメソッド削除、Closedイベントからのメソッド削除は、言わば「お約束」で、同じようなコードがそこかしこにできることになる。
・UnloadedやLoadedが何回も発生するような実装では、↑の処理はちょっと直す必要がある。
・こういう処理を必ず書いてね、と通知しても、忘れる人はいる(それが意図的ではないにしても)。
こういう「切り貼り」できるようなコードは、共通化するのが一番だと思う。
つづく。続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0

Disposableな基底クラス

IDisposableインターフェイスを実装するとき、Diposeメソッドだけを実装すれば良いなんて思ってた時期もありました。
Dispose Finalizeパターン
Dispose Finalizeパターンを実装するクラスのテンプレート
Diposeするにもいろいろあるんだなと。WPF4だと、各コントロールはIDisposableを実装していない。でも、自分が作るクラスで、イベントなんかを実装するなら、(特にMVVMパターンを使うとその頻度が高い)、どこかで破棄をしないとメモリリークの元になってしまう。結局、IDisposableインターフェイスにお世話になる。
上記を参考に、基底クラスを作ってみた。とわいえ、ほぼ"まんま"だけど。
続きを読む
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: C# | コメント: 0 | トラックバック: 0

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

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

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

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