How to force ContextMenu binding custom WPF/XAML behavior? - c#

Question:
How do we access the bound command assigned to the MenuItem inside our custom behavior? The ContextMenu isn't a part of the visual tree and doesn't bind until a click event which never occurs due to being suppressed in the custom behavior.
Purpose
I have a custom behavior using Microsoft.Xaml.Behaviors that is intended to only display the context menu if the user clicks on an object in the ListView. I want to modify the command parameter using the custom behavior while accessing the reference ICommand in the view's markup.
Code:
Custom Behavior (Microsoft.Xaml.Behaviors):
public class RightClickContextMenuBehavior : Behavior<ListView>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.ContextMenuOpening += AssociatedObject_ContextMenuOpening;
AssociatedObject.PreviewMouseRightButtonDown += AssociatedObject_PreviewMouseRightButtonDown;
AssociatedObject.PreviewMouseRightButtonUp += AssociatedObject_PreviewMouseRightButtonUp;
}
protected override void OnDetached()
{
base.OnDetached();
AssociatedObject.ContextMenuOpening -= AssociatedObject_ContextMenuOpening;
AssociatedObject.PreviewMouseRightButtonDown -=
AssociatedObject_PreviewMouseRightButtonDown;
AssociatedObject.PreviewMouseRightButtonUp -= AssociatedObject_PreviewMouseRightButtonUp;
}
private void AssociatedObject_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
//Store the click locations so that we can determine drag on mouse move.
originPoint = e.GetPosition(null);
//Grab the item from the ListView
var listViewItem = TryFindFromPoint<ListViewItem>((UIElement)sender,
e.GetPosition(AssociatedObject));
if (listViewItem == null)
return;
itemReference = listViewItem;
}
private void AssociatedObject_PreviewMouseRightButtonUp(object sender,
MouseButtonEventArgs e)
{
if (itemReference != null)
{
// Only display the context menu if the user clicked on the object
// inside the listview, not the listview itself.
var targetPosition = e.GetPosition((UIElement)itemReference);
HitTestResult hitResult = VisualTreeHelper.HitTest((UIElement)itemReference,
targetPosition);
if (hitResult != null && sender is ListView)
{
var listView = sender as ListView;
//Set the context menu's visibility flag
listView.ContextMenu.IsOpen = true;
foreach (var item in listView.ContextMenu.Items)
{
if (item is MenuItem)
{
var customMemuItem = item as MenuItem;
//Apply the item the was in our hitbox as context.
customMemuItem.CommandParameter = itemReference.DataContext;
/*
ISSUE:
Bindings here are always null, how do I force the bound
ICommand in the XAML markup to be available here. I'd like
to use the Command binding from the markup, and functionally
apply my DataContext customMemuItem.Command is null
*/
}
}
e.Handled = true; //Handle the bubble
}
}
else
{
if (sender is ListView)
{
//Hide the context menu.
var listView = sender as ListView;
listView.ContextMenu.IsOpen = false;
e.Handled = true;
}
}
}
}
View:
<ListView Grid.Row="1"
ItemsSource="{Binding Path= ItemQueue, IsAsync=True}"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<behaviors:Interaction.Behaviors>
<commandbehaviors:RightClickContextMenuBehavior />
</behaviors:Interaction.Behaviors>
<ListView.ContextMenu>
<ContextMenu DataContext="{Binding PlacementTarget.DataContext,
RelativeSource={RelativeSource Self}}">
<MenuItem Header="Context Menu Command"
Command="{Binding Source={x:Type models:MyViewModel},
Path=BindingContext.MenuTestCommand}"
CommandParameter="{Binding}"/>
</ContextMenu>
</ListView.ContextMenu>
[...]
</ListView>
Edit #1:
We're using MVVM where ListView is in:
public partial class MyViewModelView : UserControl
{
public DocumentTileManagementView()
{
InitializeComponent();
}
}
public class MyViewModel : ViewModelBase
{
public MyViewModel()
{
MenuTestCommand = new ApplicationRelayCommand(MenuTestCommandBehavior);
}
public ObservableCollection<ObjectViewModel> ItemQueue
{
get
{
return _ItemQueue;
}
set
{
this.MutateVerbose(ref _ItemQueue, value, this.RaisePropertyChanged());
}
}
private ObservableCollection<MyDataObject> _ItemQueue= new ObservableCollection<MyDataObject>();
public ICommand MenuTestCommand { get; }
private async void MenuTestCommandBehavior(object obj)
{
}
}
I haven't found a way to invoke the binding without allowing the inital right-click event chain on the ListView.
I have a working solution where I allow the first right click event and override the behavior in each subsequent click. I'd like to invoke whatever is occurring during that first right click in the custom (Microsoft.Xaml.Behaviors) behavior.

Instead of attempting to override the entire event chain I've attached to the ContextMenuOpening event. On the first PreviewMouseRightButtonUp we allow the event chain to complete binding all parameters and commands from mark up. The ContextMenuOpening event allows for initial hitbox configuration and CommandParameter assignment.
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.ContextMenuOpening += AssociatedObject_ContextMenuOpening;
[...]
}
private void AssociatedObject_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
if (!isFirstClick)
{
isFirstClick = true;
e.Handled = false;
AssociatedObject.ContextMenu.Visibility = Visibility.Collapsed;
return;
}
else
{
[...]
}
}
private void AssociatedObject_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
if (AssociatedObject.Items.Count > 0)
{
var listViewItem = TryFindFromPoint<ListViewItem>((UIElement)e.OriginalSource, new Point(e.CursorLeft, e.CursorTop));
foreach (var item in AssociatedObject.ContextMenu.Items)
{
if (item is MenuItem && listViewItem != null)
{
var customMemuItem = item as MenuItem;
customMemuItem.CommandParameter = listViewItem.DataContext;
}
}
AssociatedObject.ContextMenu.Visibility = Visibility.Visible;
AssociatedObject.ContextMenu.IsOpen = true;
}
else
{
AssociatedObject.ContextMenu.Visibility = Visibility.Collapsed;
AssociatedObject.ContextMenu.IsOpen = false;
}
}

Related

WPF ToggleButton Binding Not Working

I have two ToggleButtons; I'm trying to make them behave like a pair of radio buttons by binding them to booleans, but it's not working. Here's what I have so far:
<ToggleButton Name="YesButton" Margin="5,0" Width="100" IsChecked="{Binding YesBool, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">Yes!</ToggleButton>
<ToggleButton Name="NoButton" Margin="5,0" Width="100" IsChecked="{Binding NoBool, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">No!</ToggleButton>
And
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
}
public class Thingy : INotifyPropertyChanged
{
private bool _yesno;
public bool YesBool
{
get { return _yesno; }
set { _yesno = value; NotifyPropertyChanged("YesBool"); }
}
public bool NoBool
{
get { return !_yesno; }
set { _yesno = !value; NotifyPropertyChanged("NoBool"); }
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As far as I can tell, everyone else who had this problem misspelled their binding or didn't use NotifyPropertyChanged, but (as far as I can tell) I'm doing both of those things. What am I doing wrong?
Set your DataContext in your xaml to your Thingy class instead of "this" Window.
You're question doesn't specify if the booleans are needed or are only there to help you get the wanted behaviour.
So in case you don't need them, you could also go for a function, which unchecks the other button. This could be also used for more than 2 ToggleButtons.
If you can be certain there are no other coontrols than ToggleButtons, you could also go for a foreach loop without the type-check.
public void ToggleButtonChecked(object sender, RoutedEventArgs e)
{
ToggleButton btn = sender as ToggleButton;
if (btn == null)
return;
Panel container = btn.Parent as Panel;
if (container == null)
return;
for (int i = 0; i<container.Children.Count; i++)
{
if (container.Children[i].GetType() == typeof(ToggleButton))
{
ToggleButton item = (ToggleButton)container.Children[i];
if (item != btn && item.IsChecked == true)
item.IsChecked = false;
}
}
}
XAML
<ToggleButton x:Name="tb1" Checked="ToggleButtonChecked"/>

Setting checkbox selected items to False in User interface - C# WPF

I have a simple checkbox items and when items are selected, it works fine.
I put a button to unselect all selected items . In the debug mode, I can see the checked state being set to unchecked (false) although it is not reflected in the UI. Here is the code:
XAML for Listbox-Checkbox:
<ListBox x:Name="Listitems" Grid.Column="0" SelectionMode="Multiple" ItemsSource="{Binding MonthlyResults}" >
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding logdate}" IsChecked="{Binding Checked ,Mode=TwoWay}" Click="CheckBox_Click"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
XAML for UncheckALL button:
<Button Grid.Row="0" Name="ClearALL" Margin="4,10,4,75" Content="Unselect All" FontFamily="Tahoma" FontSize="12" Click="Button_Click"/>
Code behind:
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
var cb = sender as CheckBox;
var item = cb.DataContext;
Listitems.SelectedItem = item;
HornerPlotPluginModel model = DataContext as HornerPlotPluginModel;
var checkedItems1 = model.MonthlyResults.Where(B => B.Checked == true);
//monthlyresults is the observable collection that populates the checkbox items
model.CDFResults.Clear(); // some function
Chart1.Series.Clear();
Chart1.Axes.Clear();
model.DisplayLogs(); // some function
DrawCurves(); // some function
}
Code behind for the UncheckAll button:
private void Button_Click(object sender, RoutedEventArgs e)
{
HornerPlotPluginModel model = DataContext as HornerPlotPluginModel;
var checkedItems1 = model.MonthlyResults.Where(B => B.Checked == true);
Listitems.SelectedItems.Clear(); //SET CHECKED ITEMS TO FALSE!!!
model.CDFResults.Clear();
Chart1.Series.Clear();
}
I did look at similar post here: WPF UserControl property change not updating
but it went over my head!
Make sure that the class where the Checked property is defined implements the INotifyPropertyChanged interface and raises the PropertyChanged event in the setter of the Checked property:
public class MonthlyReport : INotifyPropertyChanged
{
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then you should be able to simply set the Checked property of all those objects to false to refresh the CheckBox:
HornerPlotPluginModel model = DataContext as HornerPlotPluginModel;
foreach(var item in model.MonthlyResults)
{
item.Checked = false;
}
HornerPlotPluginModel model = DataContext as HornerPlotPluginModel;
foreach(var item in model.MonthlyResults)
{
item.Checked = false;
}

Listbox scroll viewer - default scroll to bottom

I have a ListBox which has to be scrolled to bottom automatically. In my application, I have to detect if some items were already visibled to user and to do some business logic if so. I am using virtualization here which calls item (vm) properties only if it is visible.
For auto scroll, i am using listbox.ScrollIntoView(listbox.SelectedItem); which works perfect, the problem is that the ScrollIntoView will run only after the ListBox is already loaded and rendered which means that it first displays some items from its beginning and after that it will scroll to bottom... it is undesirable for me. I just want to scroll immediately to bottom (before the ListBox is rendered).
Here is my behavior for auto scroll to bottom:
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.SelectionChanged += AssociatedObject_SelectionChanged;
}
void AssociatedObject_SelectionChanged(object sender, EventArgs e)
{
if (sender is ListBox)
{
ListBox listbox = (sender as ListBox);
if (listbox.SelectedItem != null)
{
listbox.Dispatcher.BeginInvoke((Action)(() =>
{
listbox.UpdateLayout();
if (listbox.SelectedItem != null)
{
listbox.ScrollIntoView(listbox.SelectedItem);
}
}));
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.SelectionChanged -= AssociatedObject_SelectionChanged;
}
My ListBox is setted to IsSynchronizedWithCurrentItem="True" and its ItemsSource is binded to ICollectionView where i am using MoveCurrentToLast.
So the question is: is there any way how to scroll to bottom without rendering its top first?
I've reproduced your attached command as a
public class MyBehavior : Behavior<ListBox>
{
to a XAML
<ListBox SelectedItem="SelCust" Name="MyListBox" Loaded="MyListBox_Loaded" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name" ItemsSource="{Binding Customers}">
<i:Interaction.Behaviors>
<local:MyBehavior/>
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding Path=LoadCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
where I've also added a binding for the Load event to the ViewModel
public CustomerViewModel()
{
IList<Customer> customers = Customer.GetCustomers().ToList();
_customerView = CollectionViewSource.GetDefaultView(customers);
_customerView.MoveCurrentToLast();
_customerView.CurrentChanged += CustomerSelectionChanged;
}
private void CustomerSelectionChanged(object sender, EventArgs e)
{
// React to the changed selection
Debug.WriteLine("Here");
var sel = (sender as CollectionView).CurrentItem;
if ( sel!= null)
{
//Do Something
}
}
private DelegateCommand loadCommand;
public ICommand LoadCommand
{
get
{
if (loadCommand == null)
{
loadCommand = new DelegateCommand(VMLoad);
}
return (ICommand)loadCommand;
}
}
bool IsLoaded = false;
private void VMLoad(object obj)
{
IsLoaded = true;
}
and in the code-behind
public MainWindow()
{
InitializeComponent();
DataContext = new CustomerViewModel();
}
private void MyListBox_Loaded(object sender, RoutedEventArgs e)
{
MyListBox.ScrollIntoView(MyListBox.Items[MyListBox.Items.Count - 1]);
}
When I debug it, I see that the this is the sequence of events fired:
CurrentChanged wiht the last item of the collection
Loaded handler in the code-behind
LoadCommand in the ViewModel and only after that
ScrollIntoView from the AssociatedObject_SelectionChanged
So basically I'm suggesting a couple of things:
add (another) ScrollIntoView (for the last item of the collection) from the Loaded handler in the
code-behind
for whatever action you need to perform when you have to detect if some items are already visible to user, first check IsLoaded to exclude any transient effect
Why not simply scroll to the last value in your collection after you set the DataContext or ItemSource? No data will render until you set your data context, and until you exit the constructor. To my understanding if you do the following to steps in sequence in the constructor, it should work as expected.
listBox.DataContext = _items;
listBox.ScrollIntoView(_items.Last());

How to put a user control in a static layer ontop of all other controls?

I'm developing an autocomplete user control for WPF using XAML and C#.
I would like to have the pop-up for the suggestions to appear above all controls. Currently my pop up is a ListView . That causes problems since whenever I decide to show it the UI must find a place for it and to do so moves all the controls which are below it further down.
How can I avoid this? I assume I must put it in a layer which is above all of the other controls?
I have written "auto-complete" style controls before by using the WPF Popup control, combined with a textbox. If you use Popup it should appear, as you say, in a layer over the top of everything else. Just use Placement of Bottom to align it to the bottom of the textbox.
Here is an example that I wrote a while ago. Basically it is a text box which, as you type pops up a suggestions popup, and as you type more it refines the options down. You could fairly easily change it to support multi-word auto-complete style code editing situations if you wanted that:
XAML:
<Grid>
<TextBox x:Name="textBox"
Text="{Binding Text, Mode=TwoWay, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:IntelliSenseUserControl}}}"
KeyUp="textBox_KeyUp"/>
<Popup x:Name="popup"
Placement="Bottom"
PlacementTarget="{Binding ElementName=textBox}"
IsOpen="False"
Width="200"
Height="300">
<ListView x:Name="listView"
ItemsSource="{Binding FilteredItemsSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:IntelliSenseUserControl}}}"
SelectionChanged="ListView_Selected"/>
</Popup>
</Grid>
Code-behind:
public partial class IntelliSenseUserControl : UserControl, INotifyPropertyChanged
{
public IntelliSenseUserControl()
{
InitializeComponent();
DependencyPropertyDescriptor prop = DependencyPropertyDescriptor.FromProperty(ItemsSourceProperty, typeof(IntelliSenseUserControl));
prop.AddValueChanged(this, ItemsSourceChanged);
}
private void ItemsSourceChanged(object sender, EventArgs e)
{
FilteredItemsSource = new ListCollectionView((IList)ItemsSource);
FilteredItemsSource.Filter = (arg) => { return arg == null || string.IsNullOrEmpty(textBox.Text) || arg.ToString().Contains(textBox.Text.Trim()); };
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(IntelliSenseUserControl), new FrameworkPropertyMetadata(null) { BindsTwoWayByDefault = true });
public object ItemsSource
{
get { return (object)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(object), typeof(IntelliSenseUserControl), new PropertyMetadata(null));
#region Notified Property - FilteredItemsSource (ListCollectionView)
public ListCollectionView FilteredItemsSource
{
get { return filteredItemsSource; }
set { filteredItemsSource = value; RaisePropertyChanged("FilteredItemsSource"); }
}
private ListCollectionView filteredItemsSource;
#endregion
private void textBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return || e.Key == Key.Enter)
{
popup.IsOpen = false;
}
else
{
popup.IsOpen = true;
FilteredItemsSource.Refresh();
}
}
private void UserControl_LostFocus(object sender, RoutedEventArgs e)
{
popup.IsOpen = false;
}
private void ListView_Selected(object sender, RoutedEventArgs e)
{
if (listView.SelectedItem != null)
{
Text = listView.SelectedItem.ToString().Trim();
}
}
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
If your Window's content container is a Grid, you can simply do something like
<ListBox Grid.RowSpawn="99" Grid.ColumnSpan="99"/>
to "simulate" an absolute position. You then just have to set its position with Margin, HorizontalAlignment and VerticalAlignment so it lays around the desired control.

WPF SelectionChanged to same value

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);
}
I used DropDownClosed, thank to - Clemens
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;
}
}
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>
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)
You can bind the event handler to the onclickevent of the combobox then check if the selected item is null
TO do it you will have to add an event for mouse up for every item you added in combo box.

Categories