ComboBox_SelectionChanged イベントが WPF でどのように発生したかを伝える方法はありますか。
つまり、イベントはユーザー操作の結果として発生したのか、それともバインド先のプロパティの変更の結果として発生したのか?
ComboBox_SelectionChanged イベントが WPF でどのように発生したかを伝える方法はありますか。
つまり、イベントはユーザー操作の結果として発生したのか、それともバインド先のプロパティの変更の結果として発生したのか?
ComboBox.SelectionChanged イベントでは、送信者は常にComboBox であり、SelectionChangedEventArgs に役立つものは何もありません。
これに対する2つの解決策が思い浮かびます。バインディングでコンバーターを使用するか、スタック トレースをチェックして System.Windows.Controls.Primitives.Selector.OnSelectedItemsCollectionChanged(object, NotifyCollectionChangedArgs) がスタック内にあるかどうかを確認できます。スタック チェックは非常に見苦しく、悪い習慣であり、部分信頼環境では機能しません。というわけで、もう一方だけ説明します。
バインディングでコンバーターを使用して変更元を検出する
このソリューションは比較的クリーンですが、バインディングを変更する必要があります。また、状況が変わっていないときに通知することもあります。
ステップ 1: 変換は行わないが、「Converted」イベントと「ConvertedBack」イベントを持つコンバーターを作成します。
public EventingConverter : IValueConverter
{
public event EventHandler Converted;
public event EventHandler ConvertedBack;
public object Convert(object value, ...)
{
if(Converted!=null) Converted(this, EventArgs.Empty);
return value;
}
public object ConvertBack(object value, ...)
{
if(ConvertedBack!=null) ConvertedBack(this, EventArgs.Empty);
return value;
}
}
ステップ 2: このコンバーターの新しいインスタンスを使用するようにバインディングを設定します (通常行われているように、リソース ディクショナリまたは静的プロパティを使用してコンバーター インスタンスを共有しないでください)。
<ComboBox ...>
<ComboBox.SelectedValue>
<Binding Path="..." ...>
<Binding.Converter>
<local:EventingConverter
Converted="ComboBoxSelectedValue_Converted"
ConvertedBack="ComboBoxSelectedValue_ConvertedBack" />
</Binding.Converter>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
これで、バインド プロセス内から ComboBoxSelectedValue_Converted および ComboBoxSelectedValue_ConvertedBack メソッドが呼び出されます。
警告: これらのイベントで例外をスローすると、バインディングが壊れます。
バインディングを行う XAML を変更できない場合
バインディングを作成する XAML を制御できない場合 (たとえば、添付プロパティを使用している場合) は、後でコンバーターを追加することができます。この場合、コンバーター クラスは以前に宣言されたコンバーターにチェーンする必要があります。Binding を複製して新しいものをインストールする必要があります (一度使用すると不変になります)。また、MultiBindings も処理する必要があります。 (あなたがそれらをサポートしたい場合)。
最後の注意事項
変更がユーザーによって行われたのかプロパティによって行われたのかを判断する必要があるのは、実際には UI 設計が不十分であることの兆候である可能性があり、通常はユーザーが自分の要件を本当に理解していないことが原因です。
私が取り組んできたいくつかのプロジェクトでは、エンド ユーザーが「この ComboBox を変更すると」そのようなことが起こると指定しました。ほぼすべてのケースで、特定のユース ケースでアプリケーションが予期せぬ動作をすることが判明したため、目標を達成するためのより良い方法を見つけました。多くの場合、ユーザーが本当に望んでいたのは、「この値がデータベース内の値と最初に異なるとき」、「この値がデフォルトではなくなったとき」、または「この値が 5 になったとき」です。
私もこの種の問題に遭遇し、ブールbInternalChange
変数を使用して解決しました。
°C を °F に変換し、2 つの ComboBox で戻すインターフェイスを想像してみてください。最初の値を選択すると 2 番目の選択が更新され、2 番目の値を選択すると最初の値が更新されます。UI の変更と内部の変更を区別しないと、無限ループが発生します。
bool bInternalChange = false;
private void ComboBoxF_SelectionChanged(...)
{
if (!bInternalChange)
{
bInternalChange = true;
ComboBoxC.SelectedValue = ConvertFtoC(...);
bInternalChange = false;
}
}
private void ComboBoxC_SelectionChanged(...)
{
if (!bInternalChange)
{
bInternalChange = true;
ComboBoxF.SelectedValue = ConvertCtoF(...);
bInternalChange = false;
}
}