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>
SelectionChanged
event when the selection hasn't actually changed. There are however other events like e.g.DropDownClosed
. – Clemens