5
votes

I am using SelectionChanged in ComboBox item, but when I select the same item again the SelectionChanged function does not fire and I need it to do so. How can I tell it to fire no mater witch selection I do.

private void ComboBox_SelectionChanged4(object sender, SelectionChangedEventArgs e)
    {

      //do some stuff

    }

XAML:

<ComboBox   Height="30" Name="Combo4" Style="{StaticResource CategoryComboBox}"   SelectionChanged="ComboBox_SelectionChanged4" Grid.ColumnSpan="2"  Grid.Column="0">
            <ComboBoxItem Content="ComboBox Item 1 (Example)" />
            <ComboBoxItem Content="ComboBox Item 2 (Example)" />
            <ComboBoxItem Content="ComboBox Item 3 (Example)" />
            <ComboBoxItem Content="ComboBox Item 4 (Example)" />
        </ComboBox>

Adding items:

for (int i = 0; i < Pr4.Count(); i++)
        {
            ComboBoxItem item = new ComboBoxItem();
            item.Content = Pr4[i];
            Combo4.Items.Add(item);

        }
6
Can you show some code - ie, what does the property look like in your viewmodel (are you using a viewmodel), etc. Also please show your xaml.czuroski
It doesn't make sense to fire a SelectionChanged event when the selection hasn't actually changed. There are however other events like e.g. DropDownClosed.Clemens
Internally the ComboBox will only raise a SelectionChanged event if the selection actually changed, that's why you're not seeing that event. As Clemens says, use a different event and handle things yourself if you want to record something that isn't a change of the selected item. A possibility might be to set the selected item to null on DropDownOpen, just so that you get a SelectedItem change when something is selected.Mashton
Clemens - please post it as answer. DropDownClosed workedDim
It makes a very good sense to fire it even if it hasn't changed. E.g. in case you use the very same combo box for group and but also for group's items.Michal Pokluda

6 Answers

5
votes

I used DropDownClosed, thank to - Clemens

4
votes

I had the same question and I finally found the answer:

You need to handle BOTH the SelectionChanged event and the DropDownClosed like this:

In XAML:

<ComboBox Name="cmbSelect" SelectionChanged="ComboBox_SelectionChanged" DropDownClosed="ComboBox_DropDownClosed">
  <ComboBoxItem>1</ComboBoxItem>
  <ComboBoxItem>2</ComboBoxItem>
  <ComboBoxItem>3</ComboBoxItem>
</ComboBox>

In C#:

private bool handle = true;
private void ComboBox_DropDownClosed(object sender, EventArgs e) {
  if(handle)Handle();
  handle = true;
}

private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
  ComboBox cmb = sender as ComboBox;
  handle = !cmb.IsDropDownOpen;
  Handle();
}

private void Handle() {
  switch (cmbSelect.SelectedItem.ToString().Split(new string[] { ": " }, StringSplitOptions.None).Last())
  { 
      case "1":
          //Handle for the first combobox
          break;
      case "2":
          //Handle for the second combobox
          break;
      case "3":
          //Handle for the third combobox
          break;
  }
}
2
votes

Behavior which uses DropDownOpen and DropDwonClose

public class InvokeIfSameElementSelectedBehavior : Behavior<ComboBox>
    {
        #region public ICommand Command

        private static readonly PropertyMetadata CommandMetaData = new PropertyMetadata(default(ICommand));

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command",
            typeof(ICommand), typeof(InvokeIfSameElementSelectedBehavior), CommandMetaData);

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        #endregion //public ICommand Command

        private bool _skipSelectionChanged;
        private bool _popupMouseClicked;
        private Popup _popup;
        private object _previousValue;

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

            if(AssociatedObject.IsLoaded)
                AttachAllEvents();
            else 
                AssociatedObject.Loaded += AssociatedObjectOnLoaded;
        }

        private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            AssociatedObject.Loaded -= AssociatedObjectOnLoaded;
            AttachAllEvents();
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.SelectionChanged -= AssociatedObjectOnSelectionChanged;
            AssociatedObject.DropDownOpened -= AssociatedObjectOnDropDownOpened;
            AssociatedObject.DropDownClosed -= AssociatedObjectOnDropDownClosed;

            if(_popup != null)
                _popup.PreviewMouseLeftButtonDown -= PopupOnPreviewMouseLeftButtonDown;
        }

        private void AttachAllEvents()
        {
            AssociatedObject.SelectionChanged += AssociatedObjectOnSelectionChanged;
            AssociatedObject.DropDownOpened += AssociatedObjectOnDropDownOpened;
            AssociatedObject.DropDownClosed += AssociatedObjectOnDropDownClosed;

            AssociatedObject.ApplyTemplate();
            _popup = (Popup)AssociatedObject.Template.FindName("PART_Popup", AssociatedObject);
            if(_popup != null)
                _popup.PreviewMouseLeftButtonDown += PopupOnPreviewMouseLeftButtonDown;
        }

        private void AssociatedObjectOnDropDownOpened(object sender, EventArgs e)
        {
            _popupMouseClicked = false;
            _previousValue = AssociatedObject.SelectedItem;
        }

        private void AssociatedObjectOnDropDownClosed(object sender, EventArgs e)
        {
            try
            {
                if (_popupMouseClicked && Equals(AssociatedObject.SelectedItem, _previousValue)) //SelectionChanged handles it if value are not the same
                    InvokeChangeCommand(AssociatedObject.SelectedItem);
            }
            finally
            {
                _popupMouseClicked = false;
            }
        }

        private void PopupOnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //ignore clicks on the scrollbars
            if(e.Source is ScrollViewer)
                return;

            _popupMouseClicked = true;
        }

        private void AssociatedObjectOnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (_skipSelectionChanged)
                return;

            try
            {
                _skipSelectionChanged = true;

                if (e.AddedItems.Count != 1)
                    return;
                InvokeChangeCommand(e.AddedItems[0]);
            }
            finally
            {
                _skipSelectionChanged = false;
            }
        }

        private void InvokeChangeCommand(object item)
        {
            if (Command == null)
                return;

            if (!Command.CanExecute(item))
                return;

            Command.Execute(item);
        }
    }

Sample:

<ComboBox>
    <i:Interaction.Behaviors>
        <behaviors:InvokeIfSameElementSelectedBehavior Command="{Binding SelectionChangedCommand}"/>
    </i:Interaction.Behaviors>
</ComboBox>
1
votes

You could use the dropdown opened even to set the selection to -1. Then in the event handler you need to ignore that change. On the dropdown closed event you can restore the original value if the control is closed.(Also ignore this change)

0
votes

You can bind the event handler to the onclickevent of the combobox then check if the selected item is null

0
votes

TO do it you will have to add an event for mouse up for every item you added in combo box.