プログラムによるテキストの変更(ボタンクリックハンドラーイベントなど)とユーザー入力(テキストの入力、切り取り、貼り付け)を区別したいと思います。
出来ますか?
組み込みのWPFTextBoxを使用したいだけなら、それが可能だとは思いません。
ここのSilverlightフォーラムにも同様の議論があります: http://forums.silverlight.net/p/119128/268453.aspx まったく同じ質問ではありませんが、それに似たアイデアだと思います元の投稿であなたのためにトリックをするかもしれません。サブクラス化されたTextBoxにSetTextメソッドを設定します。このメソッドは、テキストを変更する前にフラグを設定し、後で元に戻します。次に、TextChangedイベント内のフラグを確認できます。もちろん、これには、その方法を使用するためにすべてのプログラムによるテキストの変更が必要になりますが、プロジェクトを十分に制御して、それが機能すると思うことを義務付けている場合。
TextBox
でのユーザー入力は、次のように識別できます。
これらの3つのイベントをboolフラグと組み合わせて、上記のいずれかがTextChangedイベントの前に発生したかどうかを示し、更新の理由がわかります。
入力と貼り付けは簡単ですが、Backspaceが常にTextChanged
をトリガーするとは限りません(たとえば、テキストが選択されておらず、カーソルが位置0にある場合)。したがって、PreviewTextInputにはいくつかのロジックが必要です。
これは、上記のロジックを実装し、TextChanged
が発生したときにboolフラグを指定してコマンドを実行するアタッチされた動作です。
<TextBox ex:TextChangedBehavior.TextChangedCommand="{Binding TextChangedCommand}" />
そしてコードであなたは次のようなアップデートのソースを見つけることができます
private void TextChanged_Executed(object parameter)
{
object[] parameters = parameter as object[];
object sender = parameters[0];
TextChangedEventArgs e = (TextChangedEventArgs)parameters[1];
bool userInput = (bool)parameters[2];
if (userInput == true)
{
// User input update..
}
else
{
// Binding, Programatic update..
}
}
効果を示す小さなサンプルプロジェクトを次に示します。 SourceOfTextChanged.Zip
TextChangedBehavior
public class TextChangedBehavior
{
public static DependencyProperty TextChangedCommandProperty =
DependencyProperty.RegisterAttached("TextChangedCommand",
typeof(ICommand),
typeof(TextChangedBehavior),
new UIPropertyMetadata(TextChangedCommandChanged));
public static void SetTextChangedCommand(DependencyObject target, ICommand value)
{
target.SetValue(TextChangedCommandProperty, value);
}
// Subscribe to the events if we have a valid command
private static void TextChangedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
TextBox textBox = target as TextBox;
if (textBox != null)
{
if ((e.NewValue != null) && (e.OldValue == null))
{
textBox.PreviewKeyDown += textBox_PreviewKeyDown;
textBox.PreviewTextInput += textBox_PreviewTextInput;
DataObject.AddPastingHandler(textBox, textBox_TextPasted);
textBox.TextChanged += textBox_TextChanged;
}
else if ((e.NewValue == null) && (e.OldValue != null))
{
textBox.PreviewKeyDown -= textBox_PreviewKeyDown;
textBox.PreviewTextInput -= textBox_PreviewTextInput;
DataObject.RemovePastingHandler(textBox, textBox_TextPasted);
textBox.TextChanged -= textBox_TextChanged;
}
}
}
// Catches User input
private static void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
TextBox textBox = sender as TextBox;
SetUserInput(textBox, true);
}
// Catches Backspace, Delete, Enter
private static void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.Key == Key.Return)
{
if (textBox.AcceptsReturn == true)
{
SetUserInput(textBox, true);
}
}
else if (e.Key == Key.Delete)
{
if (textBox.SelectionLength > 0 || textBox.SelectionStart < textBox.Text.Length)
{
SetUserInput(textBox, true);
}
}
else if (e.Key == Key.Back)
{
if (textBox.SelectionLength > 0 || textBox.SelectionStart > 0)
{
SetUserInput(textBox, true);
}
}
}
// Catches pasting
private static void textBox_TextPasted(object sender, DataObjectPastingEventArgs e)
{
TextBox textBox = sender as TextBox;
if (e.SourceDataObject.GetDataPresent(DataFormats.Text, true) == false)
{
return;
}
SetUserInput(textBox, true);
}
private static void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox textBox = sender as TextBox;
TextChangedFired(textBox, e);
SetUserInput(textBox, false);
}
private static void TextChangedFired(TextBox sender, TextChangedEventArgs e)
{
ICommand command = (ICommand)sender.GetValue(TextChangedCommandProperty);
object[] arguments = new object[] { sender, e, GetUserInput(sender) };
command.Execute(arguments);
}
#region UserInput
private static DependencyProperty UserInputProperty =
DependencyProperty.RegisterAttached("UserInput",
typeof(bool),
typeof(TextChangedBehavior));
private static void SetUserInput(DependencyObject target, bool value)
{
target.SetValue(UserInputProperty, value);
}
private static bool GetUserInput(DependencyObject target)
{
return (bool)target.GetValue(UserInputProperty);
}
#endregion // UserInput
}
JHunzの答えと同様に、ブール値のメンバー変数をコントロールに追加するだけです。
bool programmaticChange = false;
プログラムによる変更を行う場合は、次のようにします。
programmaticChange = true;
// insert changes to the control text here
programmaticChange = false;
イベントハンドラーでは、programmaticChange
の値を調べて、プログラムによって変更されたかどうかを判断する必要があります。
かなり明白であまりエレガントではありませんが、実行可能でシンプルです。
正確な要求に応じて、TextChanged
イベントでTextBox.IsFocused
を使用して、手動入力を決定できます。これは明らかにプログラムの変更のすべての方法をカバーするわけではありませんが、多くの例でうまく機能し、そうするためのかなりクリーンで節約的な方法です。
基本的に、これは次の場合に機能します。
...プログラムによる変更はすべて手動の変更(ボタンの押下など)に基づいています。
次の場合は機能しません。
...プログラムの変更は完全にコードに基づいています(タイマーなど)。
コード例:
textBox.TextChanged += (sender, args) =>
if (textBox.IsFocused)
{
//do something for manual input
}
else
{
//do something for programmatical input
}
}
Dodgy_coderの部分的なクレジット(あなたが望む美しいデザインに適合しないことに同意しましたが、最高の妥協点です)。あなたがカバーしたいすべてを考慮してください:
除外するものを検討してください。
コード
public class MyTB : TextBox
{
private bool _isTextProgrammaticallySet = false;
public new string Text
{
set
{
_isTextProgrammaticallySet = true;
base.Text = value;
_isTextProgrammaticallySet = false;
}
}
protected override void OnTextChanged(TextChangedEventArgs e)
{
base.OnTextChanged(e);
// .. on programmatic or on user
// .. on programmatic
if (_isTextProgrammaticallySet)
{
return;
}
// .. on user
OnTextChangedByUser(e);
}
protected void OnTextChangedByUser(TextChangedEventArgs e)
{
// Do whatever you want.
}
}
以下は推奨されませんが、すべてをカバーしようとした結果:
すべてのイベントをキャッチするための代替手段は次のとおりです。
2、4、5、6、8をカバーしようとすると、上記のより簡単で一貫性のあるソリューションを使用する必要があると考えました:)
TextChangedBehavior
クラスを Fredrik answer からクリーンアップして変更し、cutコマンドも正しく処理するようにしました(ctr+X)。
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
public class TextChangedBehavior
{
public static readonly DependencyProperty TextChangedCommandProperty =
DependencyProperty.RegisterAttached("TextChangedCommand",
typeof (ICommand),
typeof (TextChangedBehavior),
new UIPropertyMetadata(TextChangedCommandChanged));
private static readonly DependencyProperty UserInputProperty =
DependencyProperty.RegisterAttached("UserInput",
typeof (bool),
typeof (TextChangedBehavior));
public static void SetTextChangedCommand(DependencyObject target, ICommand value)
{
target.SetValue(TextChangedCommandProperty, value);
}
private static void ExecuteTextChangedCommand(TextBox sender, TextChangedEventArgs e)
{
var command = (ICommand)sender.GetValue(TextChangedCommandProperty);
var arguments = new object[] { sender, e, GetUserInput(sender) };
command.Execute(arguments);
}
private static bool GetUserInput(DependencyObject target)
{
return (bool)target.GetValue(UserInputProperty);
}
private static void SetUserInput(DependencyObject target, bool value)
{
target.SetValue(UserInputProperty, value);
}
private static void TextBoxOnPreviewExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command != ApplicationCommands.Cut)
{
return;
}
var textBox = sender as TextBox;
if (textBox == null)
{
return;
}
SetUserInput(textBox, true);
}
private static void TextBoxOnPreviewKeyDown(object sender, KeyEventArgs e)
{
var textBox = (TextBox)sender;
switch (e.Key)
{
case Key.Return:
if (textBox.AcceptsReturn)
{
SetUserInput(textBox, true);
}
break;
case Key.Delete:
if (textBox.SelectionLength > 0 || textBox.SelectionStart < textBox.Text.Length)
{
SetUserInput(textBox, true);
}
break;
case Key.Back:
if (textBox.SelectionLength > 0 || textBox.SelectionStart > 0)
{
SetUserInput(textBox, true);
}
break;
}
}
private static void TextBoxOnPreviewTextInput(object sender, TextCompositionEventArgs e)
{
SetUserInput((TextBox)sender, true);
}
private static void TextBoxOnTextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
ExecuteTextChangedCommand(textBox, e);
SetUserInput(textBox, false);
}
private static void TextBoxOnTextPasted(object sender, DataObjectPastingEventArgs e)
{
var textBox = (TextBox)sender;
if (e.SourceDataObject.GetDataPresent(DataFormats.Text, true) == false)
{
return;
}
SetUserInput(textBox, true);
}
private static void TextChangedCommandChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
var textBox = target as TextBox;
if (textBox == null)
{
return;
}
if (e.OldValue != null)
{
textBox.PreviewKeyDown -= TextBoxOnPreviewKeyDown;
textBox.PreviewTextInput -= TextBoxOnPreviewTextInput;
CommandManager.RemovePreviewExecutedHandler(textBox, TextBoxOnPreviewExecuted);
DataObject.RemovePastingHandler(textBox, TextBoxOnTextPasted);
textBox.TextChanged -= TextBoxOnTextChanged;
}
if (e.NewValue != null)
{
textBox.PreviewKeyDown += TextBoxOnPreviewKeyDown;
textBox.PreviewTextInput += TextBoxOnPreviewTextInput;
CommandManager.AddPreviewExecutedHandler(textBox, TextBoxOnPreviewExecuted);
DataObject.AddPastingHandler(textBox, TextBoxOnTextPasted);
textBox.TextChanged += TextBoxOnTextChanged;
}
}
}
正しい方向を示してくれたTimに感謝しますが、私のニーズでは、IsFocusのチェックは魅力のように機能しました。とても簡単です。
if (_queryField.IsKeyboardFocused && _queryField.IsKeyboardFocusWithin)
{
//do your things
}
else
{
//whatever
}
私もこの問題を抱えていましたが、私の場合は、Meleakのかなり複雑なソリューションを使用する代わりに、 (Preview)TextInput イベントをリッスンするだけで十分でした。プログラムによる変更も聞く必要がある場合、これは完全な解決策ではないことを理解していますが、私の場合は問題なく機能しました。