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

バイト長で制限したいときもある(参)で、直接入力をバイト数で制限した。しかし、「貼り付け」というアクションで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メソッドに渡さないとちゃんと削除できない。
本命のメソッドは以下のような感じ
/// 貼りつけコマンド実行用メソッド
/// </summary>
/// <param name="sender">イベント発生元</param>
/// <param name="e">イベントデータ</param>
private static void OnExecutePasted(object sender, ExecutedRoutedEventArgs e) {

if (!Clipboard.ContainsText()) {
//文字列以外は取り扱わない。
return;
}

var textBox = sender as TextBox;
var pastedData = Clipboard.GetText();//貼り付けられた文字列
var selectedText = textBox.SelectedText;//選択中の文字列
var currentText = textBox.Text;//現在の文字列
var SP = Environment.NewLine;//改行コード

//空文字列を貼り付けられた?
if (Utils.IsEmpty(pastedData)) {

if (Utils.IsEmpty(currentText)) {
e.Handled = true;
return;

} else if (textBox.Text == selectedText) {
//文字列選択状態で空文字列をコピペ
textBox.Text = string.Empty;
return;
}
}

if (!textBox.AcceptsTab) {
//Tabを受付ない時のみ、Tabを半角スペースに置換します。
pastedData = pastedData.Replace("\t", " ");
}

//改行コードを含むかどうかを調べます。
//TextBoxが改行を許可しない時は、最初の一行のみを貼り付け対象にします。
if (!textBox.AcceptsReturn && pastedData.Contains(SP)) {
var index = pastedData.IndexOf(SP);
pastedData = pastedData.Substring(0, index);

if (Utils.IsEmpty(pastedData)) {
//最初の一行が空文字列だったりする。
if (Utils.IsEmpty(currentText)) {
e.Handled = true;
return;

} else if (textBox.Text == selectedText) {
//文字列全選択状態で空文字列をコピペ?
textBox.Text = string.Empty;
return;
}
}
}

//現在の文字列のバイト数+貼り付けされる文字列のバイト数を計算する
if (TextBoxBehavior.GetLengthAsByte(textBox)) {
//バイト数で制限中
var currentBLength = Utils.GetByteLength(currentText);
var pastedBLength = Utils.GetByteLength(pastedData);
var selectedBLength = Utils.GetByteLength(selectedText);

if (textBox.MaxLength > 0 &&
(currentBLength + pastedBLength - selectedBLength) > textBox.MaxLength) {
//貼りつけ後の文字長が最大バイト数を超える
//最大バイト数を超えないように貼りつけ文字をカットする。
var canPasteBLength = textBox.MaxLength - currentBLength + selectedBLength;

if (canPasteBLength > 0) {
pastedData = Utils.CutByteLength(pastedData, canPasteBLength);
} else {
pastedData = string.Empty;
}
}

} else if (textBox.MaxLength > 0 &&
(textBox.Text.Length - selectedText.Length + pastedData.Length > textBox.MaxLength)) {
//文字数で制限中
//貼りつけ後の文字長が最大文字数を超える
//最大文字数を超えないように貼りつけ文字をカットする。
var canPasteLength = textBox.MaxLength - textBox.Text.Length + selectedText.Length;

if (canPasteLength > 0) {
pastedData = Utils.CutCharLength(pastedData, canPasteLength);
}
}

var resultText = string.Empty;
var caretIndex = textBox.CaretIndex;

if (Utils.IsEmpty(selectedText)) {
//選択中のテキストがない
//→カレットの位置から挿入
resultText = currentText.Insert(caretIndex, pastedData).ToString();
caretIndex += pastedData.Length;

} else if (Utils.IsEmpty(pastedData)) {
//テキストを選択中
//選択中のテキストを削除する
var start = textBox.SelectionStart;
resultText = currentText.Remove(start, selectedText.Length);

} else {
//選択中の文字列(部分)を貼り付けられた文字列に差し替える。
var start = textBox.SelectionStart;
resultText = currentText.Remove(start, selectedText.Length).
Insert(start, pastedData);
caretIndex = start + pastedData.Length;
}

textBox.Text = resultText;
textBox.CaretIndex = caretIndex;
return;
}

/// <summary>
/// ApplicationCommands.Paste用の実行可否判定用メソッド(ほぼ共通)
/// </summary>
/// <param name="sender">対象オブジェクト</param>
/// <param name="e">イベントデータ</param>
private static void OnCanExecutePasted(object sender, CanExecuteRoutedEventArgs e) {
var target = sender as TextBox;
e.CanExecute = Clipboard.ContainsText() &&
!target.IsReadOnly && target.IsEnabled && target.Visibility == Visibility.Visible;
e.Handled = true;
return;
}

これで貼り付けはどうにかなるかな。あともうひと息。
スポンサーサイト
当サイトは基本をすっ飛ばしてます。基本文法等は、@ITをどうぞ
カテゴリー: WPF4 | コメント: 0 | トラックバック: 0


この記事へのコメント

コメントの投稿

非公開コメント


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

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

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

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