web-dev-qa-db-ja.com

MVVMを使用したWPFでのコンボボックスの選択をキャンセルします

WPFアプリケーションにコンボボックスがあります。

<ComboBox  ItemsSource="{Binding CompetitorBrands}" DisplayMemberPath="Value" 
   SelectedValuePath="Key" SelectedValue="{Binding Path=CompMfgBrandID, Mode=TwoWay,
   UpdateSourceTrigger=PropertyChanged}" Text="{Binding CompMFGText}"/>

KeyValuePair<string, string>のコレクションにバインドされています

これが私のViewModelのCompMfgBrandIDプロパティです:

public string CompMfgBrandID
{
    get { return _compMFG; }
    set
    {    
        if (StockToExchange != null && StockToExchange.Where(x => !string.IsNullOrEmpty(x.EnteredPartNumber)).Count() > 0)
        {
            var dr = MessageBox.Show("Changing the competitor manufacturer will remove all entered parts from the transaction.  Proceed?",
                "Transaction Type", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
            if (dr != DialogResult.Yes)
                return;
        }

        _compMFG = value;
        StockToExchange.Clear();

        ...a bunch of other functions that don't get called when you click 'No'...
        OnPropertyChanged("CompMfgBrandID");
    }
}

「はい」を選択すると、期待どおりに動作します。アイテムがクリアされ、残りの関数が呼び出されます。 「いいえ」を選択すると、リストが返され、リストがクリアされないか、他の関数が呼び出されません。これは問題ありませんが、コンボボックスには新しい選択が表示されます。ユーザーが「いいえ」を選択したときに、何も変更されていないかのように、元の選択に戻すために必要です。どうすればこれを達成できますか?また、コードビハインドにe.Handled = trueを追加しようとしましたが、役に立ちませんでした。

26
drowned

MVVMでこれを実現するには...

1] ComboBoxのSelectionChangedイベントを処理する動作を添付します。このイベントは、Handledフラグを持ついくつかのイベント引数で発生します。ただし、trueに設定しても、SelectedValueバインディングには役に立ちません。バインディングは、イベントが処理されたかどうかに関係なく、ソースを更新します。

2]したがって、_ComboBox.SelectedValue_バインディングをTwoWayおよびExplicitに構成します。

3] checkが満たされ、メッセージボックスにYesと表示されている場合のみ、BindingExpression.UpdateSource()を実行します。それ以外の場合は、単にBindingExpression.UpdateTarget()を呼び出して、古い選択に戻します。


以下の例では、ウィンドウのデータコンテキストにバインドされた_KeyValuePair<int, int>_のリストがあります。 _ComboBox.SelectedValue_は、MyKeyの単純な書き込み可能なWindowプロパティにバインドされています。

XAML ...

_    <ComboBox ItemsSource="{Binding}"
              DisplayMemberPath="Value"
              SelectedValuePath="Key"
              SelectedValue="{Binding MyKey,
                                      ElementName=MyDGSampleWindow,
                                      Mode=TwoWay,
                                      UpdateSourceTrigger=Explicit}"
              local:MyAttachedBehavior.ConfirmationValueBinding="True">
    </ComboBox>
_

ここで、MyDGSampleWindowWindowのx:Nameです。

コードビハインド...

_public partial class Window1 : Window
{
    private List<KeyValuePair<int, int>> list1;

    public int MyKey
    {
        get; set;
    }

    public Window1()
    {
        InitializeComponent();

        list1 = new List<KeyValuePair<int, int>>();
        var random = new Random();
        for (int i = 0; i < 50; i++)
        {
            list1.Add(new KeyValuePair<int, int>(i, random.Next(300)));
        }

        this.DataContext = list1;
    }
 }
_

そして添付の動作

_public static class MyAttachedBehavior
{
    public static readonly DependencyProperty
        ConfirmationValueBindingProperty
            = DependencyProperty.RegisterAttached(
                "ConfirmationValueBinding",
                typeof(bool),
                typeof(MyAttachedBehavior),
                new PropertyMetadata(
                    false,
                    OnConfirmationValueBindingChanged));

    public static bool GetConfirmationValueBinding
        (DependencyObject depObj)
    {
        return (bool) depObj.GetValue(
                        ConfirmationValueBindingProperty);
    }

    public static void SetConfirmationValueBinding
        (DependencyObject depObj,
        bool value)
    {
        depObj.SetValue(
            ConfirmationValueBindingProperty,
            value);
    }

    private static void OnConfirmationValueBindingChanged
        (DependencyObject depObj,
        DependencyPropertyChangedEventArgs e)
    {
        var comboBox = depObj as ComboBox;
        if (comboBox != null && (bool)e.NewValue)
        {
            comboBox.Tag = false;
            comboBox.SelectionChanged -= ComboBox_SelectionChanged;
            comboBox.SelectionChanged += ComboBox_SelectionChanged;
        }
    }

    private static void ComboBox_SelectionChanged(
        object sender, SelectionChangedEventArgs e)
    {
        var comboBox = sender as ComboBox;
        if (comboBox != null && !(bool)comboBox.Tag)
        {
            var bndExp
                = comboBox.GetBindingExpression(
                    Selector.SelectedValueProperty);

            var currentItem
                = (KeyValuePair<int, int>) comboBox.SelectedItem;

            if (currentItem.Key >= 1 && currentItem.Key <= 4
                && bndExp != null)
            {
                var dr
                    = MessageBox.Show(
                        "Want to select a Key of between 1 and 4?",
                        "Please Confirm.",
                        MessageBoxButton.YesNo,
                        MessageBoxImage.Warning);
                if (dr == MessageBoxResult.Yes)
                {
                    bndExp.UpdateSource();
                }
                else
                {
                    comboBox.Tag = true;
                    bndExp.UpdateTarget();
                    comboBox.Tag = false;
                }
            }
        }
    }
}
_

動作では、_ComboBox.Tag_プロパティを使用して、選択した古い値に戻ったときに再チェックをスキップするフラグを一時的に保存します。

これが役立つかどうか教えてください。

17
WPF-it

これは、Blendの Generic Behavior を使用して、一般的でコンパクトな方法で実現できます。

この動作では、SelectedItemという名前の依存関係プロパティが定義されているため、ComboBoxのSelectedItemプロパティではなく、このプロパティにバインディングを配置する必要があります。この動作は、依存関係プロパティの変更をComboBox(またはより一般的にはセレクター)に渡すことを担当し、セレクターのSelectedItemが変更されると、それを独自のSelectedItemプロパティ。割り当てが失敗した場合(おそらくバウンドVMプロパティセッターが割り当てを拒否したため))、動作はセレクターのSelectedItemをそのSelectedItemプロパティの現在の値で更新します。

さまざまな理由で、セレクター内のアイテムのリストがクリアされ、選択されたアイテムがnullになる場合があります( この質問 を参照)。通常、この場合、VMプロパティがnullになることは望ましくありません。このために、デフォルトでtrueであるIgnoreNullSelection依存関係プロパティを追加しました。これでこのような問題が解決するはずです。

これはCancellableSelectionBehaviorクラスです。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

namespace MySampleApp
{
    internal class CancellableSelectionBehavior : Behavior<Selector>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.SelectionChanged += OnSelectionChanged;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectionChanged -= OnSelectionChanged;
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(CancellableSelectionBehavior),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));

        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty IgnoreNullSelectionProperty =
            DependencyProperty.Register("IgnoreNullSelection", typeof(bool), typeof(CancellableSelectionBehavior), new PropertyMetadata(true));

        /// <summary>
        /// Determines whether null selection (which usually occurs since the combobox is rebuilt or its list is refreshed) should be ignored.
        /// True by default.
        /// </summary>
        public bool IgnoreNullSelection
        {
            get { return (bool)GetValue(IgnoreNullSelectionProperty); }
            set { SetValue(IgnoreNullSelectionProperty, value); }
        }

        /// <summary>
        /// Called when the SelectedItem dependency property is changed.
        /// Updates the associated selector's SelectedItem with the new value.
        /// </summary>
        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behavior = (CancellableSelectionBehavior)d;

            // OnSelectedItemChanged can be raised before AssociatedObject is assigned
            if (behavior.AssociatedObject == null)
            {
                System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
                {
                    var selector = behavior.AssociatedObject;
                    selector.SelectedValue = e.NewValue;
                }));
            }
            else
            {
                var selector = behavior.AssociatedObject;
                selector.SelectedValue = e.NewValue;
            }
        }

        /// <summary>
        /// Called when the associated selector's selection is changed.
        /// Tries to assign it to the <see cref="SelectedItem"/> property.
        /// If it fails, updates the selector's with  <see cref="SelectedItem"/> property's current value.
        /// </summary>
        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (IgnoreNullSelection && (e.AddedItems == null || e.AddedItems.Count == 0)) return;
            SelectedItem = AssociatedObject.SelectedItem;
            if (SelectedItem != AssociatedObject.SelectedItem)
            {
                AssociatedObject.SelectedItem = SelectedItem;
            }
        }
    }
}

これは、XAMLで使用する方法です。

<Window x:Class="MySampleApp.MainWindow"
        xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
        Title="My Smaple App" Height="350" Width="525"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.Microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:MySampleApp"
        xmlns:i="http://schemas.Microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance local:MainWindowViewModel}">
    <StackPanel>
        <ComboBox ItemsSource="{Binding Options}">
            <i:Interaction.Behaviors>
                <local:CancellableSelectionBehavior SelectedItem="{Binding Selected}" />
            </i:Interaction.Behaviors>
        </ComboBox>
    </StackPanel>
</Window>

これはVMプロパティのサンプルです:

private string _selected;

public string Selected
{
    get { return _selected; }
    set
    {
        if (IsValidForSelection(value))
        {
            _selected = value;
        }
    }
}
18
splintor

.NET 4.5.1+の非常にシンプルなソリューション:

<ComboBox SelectedItem="{Binding SelectedItem, Delay=10}" ItemsSource="{Binding Items}"  />

ほとんどの場合、それは私にとってはうまくいきます。コンボボックスで選択をロールバックできます。値を割り当てずにNotifyPropertyChangedを起動するだけです。

18
Dvor_nik

私は別のスレッドでユーザーshaunによるこの質問へのはるかに簡単な答えを見つけました: https://stackoverflow.com/a/6445871/2340705

基本的な問題は、プロパティ変更イベントが飲み込まれることです。これをバグと呼ぶ人もいます。これを回避するには、ディスパッチャーからBeginInvokeを使用して、プロパティ変更イベントをUIイベントキューの最後に強制的に戻します。これには、xamlの変更、追加の動作クラス、およびビューモデルへの1行のコードの変更は必要ありません。

6
CNad

問題は、WPFがプロパティセッターで値を更新すると、その呼び出し内からのプロパティ変更通知を無視することです。これは、セッターの通常の部分として発生し、実際にプロパティを元の値に更新しました。

これを回避する方法は、フィールドを更新できるようにするだけでなく、変更を「元に戻す」ためにディスパッチャーのアクションをキューに入れることでした。このアクションは、それを古い値に戻し、プロパティ変更通知を起動して、WPFに、それが実際には新しい値ではないと認識させます。

明らかに、「元に戻す」アクションは、プログラムでビジネスロジックを起動しないように設定する必要があります。

4
RandomEngy

同じ問題がありました。UIスレッドと入札の仕組みが原因です。このリンクを確認してください: ComboBoxのSelectedItem

サンプルの構造はコードビハインドを使用していますが、MVVMはまったく同じです。

2
Fred Jand

これが私が使用する一般的なフローです(動作やXAMLの変更は必要ありません):

  1. 変更をViewModelに渡して、以前に渡されたものを追跡します。 (ビジネスロジックで、選択したアイテムが無効な状態にならないようにする必要がある場合は、モデル側に移動することをお勧めします)。このアプローチは、ラジオボタンを使用してレンダリングされるリストボックスにも適しています。SelectedItemセッターをできるだけ早く終了しても、メッセージボックスがポップアップしたときにラジオボタンが強調表示されるのを防ぐことはできません。
  2. 渡された値に関係なく、すぐにOnPropertyChangedイベントを呼び出します。
  3. 元に戻すロジックをハンドラーに入れ、SynchronizationContext.Post()を使用して呼び出します(BTW:SynchronizationContext.PostはWindowsストアアプリでも機能します。したがって、ViewModelコードを共有している場合でも、このアプローチは機能します)。

    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        public List<string> Items { get; set; }
    
        private string _selectedItem;
        private string _previouslySelectedItem;
        public string SelectedItem
        {
            get
            {
                return _selectedItem;
            }
            set
            {
                _previouslySelectedItem = _selectedItem;
                _selectedItem = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
                }
                SynchronizationContext.Current.Post(selectionChanged, null);
            }
        }
    
        private void selectionChanged(object state)
        {
            if (SelectedItem != Items[0])
            {
                MessageBox.Show("Cannot select that");
                SelectedItem = Items[0];
            }
        }
    
        public ViewModel()
        {
            Items = new List<string>();
            for (int i = 0; i < 10; ++i)
            {
                Items.Add(string.Format("Item {0}", i));
            }
        }
    }
    
1

私はスプリンターが上に持っているのと同じようにそれをしました。

あなたの見解:

<ComboBox  
ItemsSource="{Binding CompetitorBrands}" 
DisplayMemberPath="Value" 
SelectedValuePath="Key" 
SelectedValue="{Binding Path=CompMfgBrandID, 
Mode=TwoWay,
UpdateSourceTrigger=Explicit}" //to indicate that you will call UpdateSource() manually to get the property "CompMfgBrandID" udpated 
SelectionChanged="ComboBox_SelectionChanged"  //To fire the event from the code behind the view
Text="{Binding CompMFGText}"/>

以下は、ビューの背後にあるコードファイルからのイベントハンドラー「ComboBox_SelectionChanged」のコードです。たとえば、ビューがmyview.xamlの場合、このイベントハンドラーのコードファイル名はmyview.xaml.csである必要があります。

private int previousSelection = 0; //Give it a default selection value

private bool promptUser true; //to be replaced with your own property which will indicates whether you want to show the messagebox or not.

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ComboBox comboBox = (ComboBox) sender;
            BindingExpression be = comboBox.GetBindingExpression(ComboBox.SelectedValueProperty);

            if (comboBox.SelectedValue != null && comboBox.SelectedIndex != previousSelection)
            {
                if (promptUser) //if you want to show the messagebox..
                {
                    string msg = "Click Yes to leave previous selection, click No to stay with your selection.";
                    if (MessageBox.Show(msg, "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) //User want to go with the newest selection
                    {

                        be.UpdateSource(); //Update the property,so your ViewModel will continue to do something
                        previousSelection = (int)comboBox.SelectedIndex;  
                    }
                    else //User have clicked No to cancel the selection
                    {
                        comboBox.SelectedIndex = previousSelection; //roll back the combobox's selection to previous one
                    }
                }
                else //if don't want to show the messagebox, then you just have to update the property as normal.
                {
                    be.UpdateSource();
                    previousSelection = (int)comboBox.SelectedIndex;
                }
            }
        }
1
Yongquan

私は「AngelWPF」よりも「splintor」のコードサンプルを好みます。しかし、彼らのアプローチはかなり似ています。添付の動作CancellableSelectionBehaviorを実装しましたが、宣伝どおりに機能します。おそらく、splintorの例のコードが私のアプリケーションにプラグインするのが簡単だったというだけのことでしょう。 AngelWPFの添付動作のコードには、さらにコードの変更を必要とするKeyValuePairタイプへの参照が含まれていました。

私のアプリケーションでは、DataGridに表示されるアイテムがComboBoxで選択されたアイテムに基づいているComboBoxがありました。ユーザーがDataGridに変更を加えてから、ComboBoxで新しい項目を選択した場合、オプションとして[はい]、[いいえ]、[キャンセル]ボタンを使用して変更を保存するようにユーザーに促します。彼らがキャンセルを押した場合、私はComboBoxでの新しい選択を無視し、古い選択を保持したいと思いました。これはチャンピオンのように機能しました!

BlendとSystem.Windows.Interactivityへの参照を目にした瞬間に怖がる人は、Microsoft ExpressionBlendをインストールする必要はありません。 Blend SDK for .NET 4(またはSilverlight)をダウンロードできます。

Blend SDK for .NET 4

Silverlight 4用のブレンドSDK

そうそう、私のXAMLでは、この例のBlendの名前空間宣言として実際にこれを使用しています。

xmlns:i="clr-namespace:System.Windows.Interactivity;Assembly=System.Windows.Interactivity"
1
Andy

完了したい splintorの答えOnSelectedItemChangedの初期化の遅延に関する問題に遭遇したため:

AssociatedObjectが割り当てられる前にOnSelectedItemChangedが発生すると、System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvokeを使用すると、コンボボックス選択のデフォルト値でnewValueを初期化しようとするなど、望ましくない副作用が発生する可能性があります。

したがって、ViewModelが最新であっても、この動作により、ViewModelのSelectedItemの現在の値から、e.NewValueに格納されているComboBoxのデフォルトの選択への変更がトリガーされます。コードがダイアログボックスをトリガーすると、変更がない場合でもユーザーに変更が警告されます。なぜそれが起こるのか、おそらくタイミングの問題を説明することはできません。

これが私の修正です

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;

namespace MyApp
{
    internal class CancellableSelectionBehaviour : Behavior<Selector>
    {
        protected override void OnAttached()
        {
            base.OnAttached();

            if (MustPerfomInitialChange)
            {
                OnSelectedItemChanged(this, InitialChangeEvent);
                MustPerfomInitialChange = false;
            }

            AssociatedObject.SelectionChanged += OnSelectionChanged;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            AssociatedObject.SelectionChanged -= OnSelectionChanged;
        }

        public static readonly DependencyProperty SelectedItemProperty =
            DependencyProperty.Register("SelectedItem", typeof(object), typeof(CancellableSelectionBehaviour),
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));

        public object SelectedItem
        {
            get { return GetValue(SelectedItemProperty); }
            set { SetValue(SelectedItemProperty, value); }
        }

        public static readonly DependencyProperty IgnoreNullSelectionProperty =
            DependencyProperty.Register("IgnoreNullSelection", typeof(bool), typeof(CancellableSelectionBehaviour), new PropertyMetadata(true));

        /// <summary>
        /// Determines whether null selection (which usually occurs since the combobox is rebuilt or its list is refreshed) should be ignored.
        /// True by default.
        /// </summary>
        public bool IgnoreNullSelection
        {
            get { return (bool)GetValue(IgnoreNullSelectionProperty); }
            set { SetValue(IgnoreNullSelectionProperty, value); }
        }

        /// <summary>
        /// OnSelectedItemChanged can be raised before AssociatedObject is assigned so we must delay the initial change. 
        /// Using System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke has unwanted side effects.
        /// So we use this bool to know if OnSelectedItemChanged must be called afterwards, in OnAttached
        /// </summary>
        private bool MustPerfomInitialChange { get; set; }

        /// <summary>
        /// OnSelectedItemChanged can be raised before AssociatedObject is assigned so we must delay the initial change. 
        /// Using System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke has unwanted side effects.
        /// So we use this DependencyPropertyChangedEventArgs to save the argument needed to call OnSelectedItemChanged.
        /// </summary>
        private DependencyPropertyChangedEventArgs InitialChangeEvent { get; set; }

        /// <summary>
        /// Called when the SelectedItem dependency property is changed.
        /// Updates the associated selector's SelectedItem with the new value.
        /// </summary>
        private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var behavior = (CancellableSelectionBehaviour)d;

            // OnSelectedItemChanged can be raised before AssociatedObject is assigned so we must delay the initial change.
            if (behavior.AssociatedObject == null)
            {
                behavior.InitialChangeEvent = e;
                behavior.MustPerfomInitialChange = true;               
            }
            else
            {
                var selector = behavior.AssociatedObject;
                selector.SelectedValue = e.NewValue;               
            }
        }

        /// <summary>
        /// Called when the associated selector's selection is changed.
        /// Tries to assign it to the <see cref="SelectedItem"/> property.
        /// If it fails, updates the selector's with  <see cref="SelectedItem"/> property's current value.
        /// </summary>
        private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (IgnoreNullSelection && (e.AddedItems == null || e.AddedItems.Count == 0)) return;
            SelectedItem = AssociatedObject.SelectedItem;
            if (SelectedItem != AssociatedObject.SelectedItem)
            {
                AssociatedObject.SelectedItem = SelectedItem;
            }
        }
    }
}
0
GoJiTa972

問題は、バインドされたプロパティ値を設定した後、ComboBoxがユーザーアクションの結果として選択されたアイテムを設定することだと思います。したがって、Comboboxアイテムは、ViewModelで何をしても変更されます。 MVVMパターンを曲げる必要がない別のアプローチを見つけました。これが私の例です(私のプロジェクトからコピーされており、上記の例と完全には一致していません):

public ObservableCollection<StyleModelBase> Styles { get; }

public StyleModelBase SelectedStyle {
  get { return selectedStyle; }
  set {
    if (value is CustomStyleModel) {
      var buffer = SelectedStyle;
      var items = Styles.ToList();
      if (openFileDialog.ShowDialog() == true) {
        value.FileName = openFileDialog.FileName;
      }
      else {
        Styles.Clear();
        items.ForEach(x => Styles.Add(x));
        SelectedStyle = buffer;
        return;
      }
    }
    selectedStyle = value;
    OnPropertyChanged(() => SelectedStyle);
  }
}

違いは、アイテムコレクションを完全にクリアしてから、以前に保存したアイテムを入力することです。これにより、ObservableCollectionジェネリッククラスを使用しているため、Comboboxが強制的に更新されます。次に、選択したアイテムを以前に設定した選択したアイテムに戻します。コンボボックスのクリアと入力にはコストがかかるため、これは多くのアイテムにはお勧めできません。

0

--Xaml

 <ComboBox SelectedItem="{Binding SelectedItem, Mode=TwoWay, Delay=10}" ItemsSource="{Binding Items}"  />

--ViewModel

private object _SelectedItem;
public object SelectedItem 
{
    get { return _SelectedItem;}
    set {
           if(_SelectedItem == value)// avoid rechecking cause Prompt msg
            { 
               return;
            } 
            MessageBoxResult result = MessageBox.Show
                    ("Continue change?", MessageBoxButton.YesNo);
            if (result == MessageBoxResult.No)
            {
                ComboBox combo = (ComboBox)sender;
                handleSelection = false;
                combo.SelectedItem = e.RemovedItems[0];
                return;
            }
            _SelectedItem = value;
            RaisePropertyChanged(); 
        }
}
0
nap