web-dev-qa-db-ja.com

ComboBox SelectionChangedイベントをキャンセルする方法は?

ユーザーにコンボボックスの選択の変更を確認し、ユーザーが「いいえ」を選択した場合は変更を処理しないように促す簡単な方法はありますか?

選択を変更するとデータが失われるコンボボックスがあります。基本的に、ユーザーはタイプを選択すると、そのタイプの属性を入力できます。それらがタイプを変更する場合、適用されなくなる可能性があるため、すべての属性をクリアします。問題は、選択の下でSelectionChangedイベントを再度発生させることです。

これがスニペットです:

if (e.RemovedItems.Count > 0)
{
    result = MessageBox.Show("Do you wish to continue?", 
        "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning);

    if (result == MessageBoxResult.No)
    {
        if (e.RemovedItems.Count > 0)
            ((ComboBox)sender).SelectedItem = e.RemovedItems[0];
        else
            ((ComboBox)sender).SelectedItem = null;
    }
}

私には2つの解決策がありますが、どちらも好きではありません。

  1. ユーザーが'No'を選択した後、SelectionChangedイベントハンドラーを削除し、選択したアイテムを変更してから、SelectionChangedイベントハンドラーを再登録します。つまり、クラスのイベントハンドラーの参照を保持して、追加および削除できるようにする必要があります。

  2. クラスの一部としてProcessSelectionChangedブール値を作成します。イベントハンドラの開始時に必ず確認してください。選択を元に戻す前にfalseに設定し、後でtrueにリセットします。これは機能しますが、基本的にイベントハンドラーを無効にするためにフラグを使用するのは好きではありません。

誰かが私が言及したものの代替ソリューションまたは改善がありますか?

22
WPFNewbie

しばらく前にこれを行う必要があったことを覚えています。良い解決策が見つかるまでに、約1週間の調査と試行が必要でした。ここに投稿しました:

WPF:データバインドされたリストボックスでのユーザー選択をキャンセルしますか?

参考までに、これはM-V-VMベースのソリューションです(M-V-VMパターンを使用していない場合は、使用する必要があります)。

18
Aphex

私はこの良い実装を見つけました。

 private bool handleSelection=true;

private void ComboBox_SelectionChanged(object sender,
                                        SelectionChangedEventArgs e)
        {
            if (handleSelection)
            {
                MessageBoxResult result = MessageBox.Show
                        ("Continue change?", MessageBoxButton.YesNo);
                if (result == MessageBoxResult.No)
                {
                    ComboBox combo = (ComboBox)sender;
                    handleSelection = false;
                    combo.SelectedItem = e.RemovedItems[0];
                    return;
                }
            }
            handleSelection = true;
        }

ソース: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html

21
Jaider

多分ComboBoxから派生したクラスを作成し、OnSelectedItemChanged(またはOnSelectionChangeCommitted。)をオーバーライドします。

1
ispiro

WPFでは、オブジェクトを動的に設定します

    if (sender.IsMouseCaptured)
    {
      //perform operation
    }
1
Subhash Saini

SelectionChangedイベントハンドラー内で検証すると、選択が無効な場合にロジックをキャンセルできますが、イベントまたはアイテムの選択をキャンセルする簡単な方法がわかりません。

私の解決策は、WPFコンボボックスをサブクラス化し、SelectionChangedイベントの内部ハンドラーを追加することでした。イベントが発生するたびに、代わりに私のプライベート内部ハンドラーがカスタムSelectionChangingイベントを発生させます。

Cancelプロパティが対応するSelectionChangingEventArgsに設定されている場合、イベントは発生せず、SelectedIndexは以前の値に戻ります。それ以外の場合は、ベースイベントをシャドウする新しいSelectionChangedが発生します。うまくいけば、これが役立ちます!


EventArgsおよびSelectionChangingイベントのハンドラーデリゲート:

public class SelectionChangingEventArgs : RoutedEventArgs
{
    public bool Cancel { get; set; }
}

public delegate void 
SelectionChangingEventHandler(Object sender, SelectionChangingEventArgs e);

ChangingComboBoxクラスの実装:

public class ChangingComboBox : ComboBox
{
    private int _index;
    private int _lastIndex;
    private bool _suppress;

    public event SelectionChangingEventHandler SelectionChanging;
    public new event SelectionChangedEventHandler SelectionChanged;

    public ChangingComboBox()
    {
        _index = -1;
        _lastIndex = 0;
        _suppress = false;
        base.SelectionChanged += InternalSelectionChanged;
    }

    private void InternalSelectionChanged(Object s, SelectionChangedEventArgs e)
    {
        var args = new SelectionChangingEventArgs();
        OnSelectionChanging(args);
        if(args.Cancel)
        {
            return;
        }
        OnSelectionChanged(e);
    }

    public new void OnSelectionChanged(SelectionChangedEventArgs e)
    {
        if (_suppress) return;

        // The selection has changed, so _index must be updated
        _index = SelectedIndex;
        if (SelectionChanged != null)
        {
            SelectionChanged(this, e);
        }
    }

    public void OnSelectionChanging(SelectionChangingEventArgs e)
    {
        if (_suppress) return;

        // Recall the last SelectedIndex before raising SelectionChanging
        _lastIndex = (_index >= 0) ? _index : SelectedIndex;
        if(SelectionChanging == null) return;

        // Invoke user event handler and revert to last 
        // selected index if user cancels the change
        SelectionChanging(this, e);
        if (e.Cancel)
        {
            _suppress = true;
            SelectedIndex = _lastIndex;
            _suppress = false;
        }
    }
}
1
Rob

ディスパッチャーを使用してプロパティの更新を投稿(または遅延)することは良い解決策ではないと思います。これは、実際には必要のない回避策です。次のソリューションは完全にmvvmで、ディスパッチャーは必要ありません。

  • 最初に、明示的なバインドモードでSelectedItemをバインドします。 //これにより、pdateSource()メソッドを使用してコミット VMへの変更またはpdateTarget()メソッドを使用して元に戻す UI内。
  • 次に、変更が許可されているかどうかを確認するメソッドをVMに追加します(このメソッドには、ユーザーの確認を要求してブール値を返すサービスを含めることができます)。

ビューのコードビハインドでSelectionChangedイベントにフックし、VM.ConfirmChange(...)メソッドが次のように値を返したかどうかに従って、ソース(つまり、VM)またはターゲット(つまり、V)を更新します。

    private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if(e.AddedItems.Count != 0)
        {
            var selectedItem = e.AddedItems[0];
            if (e.AddedItems[0] != _ViewModel.SelectedFormatType)
            {
                var comboBoxSelectedItemBinder = _TypesComboBox.GetBindingExpression(Selector.SelectedItemProperty); //_TypesComboBox is the name of the ComboBox control
                if (_ViewModel.ConfirmChange(selectedItem))
                {
                    // Update the VM.SelectedItem property if the user confirms the change.
                    comboBoxSelectedItemBinder.UpdateSource();
                }
                else
                {
                    //otherwise update the view in accordance to the VM.SelectedItem property 
                    comboBoxSelectedItemBinder.UpdateTarget();
                }
            }
        }
    }
0
Ahmad