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.
Related
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;
}
}
I'm trying to create a User control for all the buttons on my homepage, every user control should have a different Click event. I'm trying to solve this by adding a property to the User control (which works for the label and image) but i can't find any solution for the Click event.
ImageLabelButton.xaml:
<UserControl x:Class="SC.UI.WPF.Controls.ImageLabelButton"
<Grid>
<Button Name="BtnClick">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
</Grid>
ImageLabelButton.xaml.cs:
....
<!-- works -->
public string Name
{
get { return this.LabelButton.Content.ToString(); }
set { this.LabelButton.Content = value; }
}
<!-- works -->
public ImageSource SetSource
{
get { return ImageButton.Source; }
set { ImageButton.Source = value; }
}
<!-- doesn't work -->
public EventHandler ButtonAction
{
get { return BtnClick.Click; }
set { BtnClick.Click= value; }
}
Implementation.xaml:
....
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click1"/>
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click2"/>
<controls:ImageLabelButton Name="first" SetSource="test.png" ButtonAction="Click3"/>
You just need to set the event handler up as a property of the user control and set it in the usage code:
User control XAML:
<Grid>
<Button Name="BtnClick" Click="BtnClick_DoClick">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
</Grid>
User control codebehind:
public EventHandler<RoutedEventArgs> ButtonAction;
private void BtnClick_DoClick(object sender, RoutedEventArgs e)
{
if (ButtonAction != null) ButtonAction(sender, e);
}
Implementation XAML:
<controls:ImageLabelButton Name="first1" SetSource="test.png" />
<controls:ImageLabelButton Name="first2" SetSource="test.png" />
<controls:ImageLabelButton Name="first3" SetSource="test.png" />
Implementation code behind:
public MainWindow()
{
InitializeComponent();
first1.ButtonAction = Button1;
first2.ButtonAction = Button2;
first3.ButtonAction = Button3;
}
private void Button3(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 3"); }
private void Button2(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 2"); }
private void Button1(object sender, RoutedEventArgs e) { MessageBox.Show("Pressed 1"); }
Although you can't set the event handling in XAML this way.
You need a RoutedEvent on your user control that can be accessed from Implementation.xaml
ImageLabelButton.xaml
<Button Name="BtnClick" Click="Submit_Click">
<StackPanel>
<Image Name="ImageButton"/>
<Label Name="LabelButton"/>
</StackPanel>
</Button>
ImageLabelButton.cs
private void Submit_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(ClickEvent, this));
}
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(
"Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyUserControl));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
Implementation.xaml
<controls:ImageLabelButton Name="first" SetSource="test.png" Click="Click1"/>
Implementation.xaml.cs
private void Click1(object sender, RoutedEventArgs e)
{
}
I've made a simple custom control with ClickEvent:
ImageButton.xaml:
<UserControl x:Name="ImgButton" x:Class="WpfApplication1.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid></Grid>
</UserControl>
ImageButton.xaml.cs:
public partial class ImageButton : UserControl
{
private bool mouse_down = false;
private bool mouse_in = false;
public event EventHandler Click;
public ImageButton()
{
this.MouseEnter += new MouseEventHandler(ImageButton_MouseEnter);
this.MouseLeave += new MouseEventHandler(ImageButton_MouseLeave);
this.MouseLeftButtonDown += new MouseButtonEventHandler(ImageButton_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(ImageButton_MouseLeftButtonUp);
InitializeComponent();
}
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down)&&(mouse_in))
{
Click(this, null);
}
mouse_down = false;
}
void ImageButton_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
mouse_down = true;
}
void ImageButton_MouseLeave(object sender, MouseEventArgs e)
{
mouse_in = false;
}
void ImageButton_MouseEnter(object sender, MouseEventArgs e)
{
mouse_in = true;
}
}
It works correct when I click on the control if I'm handling Click event, otherwise I get crash. So, what should I do?
You must check if handler was added to event beforre rising it:
void ImageButton_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if ((mouse_down) && (mouse_in) && Click != null)
{
Click(this, null);
}
mouse_down = false;
}
A have ListBox and 4 Items.
2 visible
2 colpased:
Click:
-this bad!
I need this:
I need Set in reapeatButton change Interval!?!? how to do it
What you want is for the list box to scroll by two lines for every one time you click the repeat buttons. Here is a behavior that you can add to your ListBox that will do just that.
First add this namespace:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
and the corresponding reference to your project.
Then the XAML looks like this:
<ListBox ScrollViewer.VerticalScrollBarVisibility="Visible" Height="40">
<i:Interaction.Behaviors>
<local:ScrollBehavior LineMultiplier="2"/>
</i:Interaction.Behaviors>
<ListBoxItem Content="Item1"/>
<ListBoxItem Content="Item2"/>
<ListBoxItem Content="Item3"/>
<ListBoxItem Content="Item4"/>
</ListBox>
and here is the behavior:
class ScrollBehavior : Behavior<FrameworkElement>
{
public int LineMultiplier
{
get { return (int)GetValue(LineMultiplierProperty); }
set { SetValue(LineMultiplierProperty, value); }
}
public static readonly DependencyProperty LineMultiplierProperty =
DependencyProperty.Register("LineMultiplier", typeof(int), typeof(ScrollBehavior), new UIPropertyMetadata(1));
protected override void OnAttached()
{
AssociatedObject.Loaded += new RoutedEventHandler(AssociatedObject_Loaded);
}
private ScrollViewer scrollViewer;
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
scrollViewer = GetScrollViewer(AssociatedObject);
scrollViewer.CommandBindings.Add(new CommandBinding(ScrollBar.LineUpCommand, LineCommandExecuted));
scrollViewer.CommandBindings.Add(new CommandBinding(ScrollBar.LineDownCommand, LineCommandExecuted));
}
private void LineCommandExecuted(object sender, ExecutedRoutedEventArgs e)
{
if (e.Command == ScrollBar.LineUpCommand)
{
for (int i = 0; i < LineMultiplier; i++)
scrollViewer.LineUp();
}
if (e.Command == ScrollBar.LineDownCommand)
{
for (int i = 0; i < LineMultiplier; i++)
scrollViewer.LineDown();
}
}
private ScrollViewer GetScrollViewer(DependencyObject o)
{
if (o is ScrollViewer)
return o as ScrollViewer;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var result = GetScrollViewer(VisualTreeHelper.GetChild(o, i));
if (result != null)
return result;
}
return null;
}
}
I had an application which required me to scroll per item so setting scrollviewer.isvirtualizing to true was enough.
However, I needed to implement a similar behavior with a dockpanel, so I used Rick Sladkey's method to accomplish what I needed.
I have a ListBox with ItemsTemplate set.
Now I want to catch Ctrl + Left click on a ListBoxItem.
I found KeyBoard class that should give me modifier keys. Now how do I get the click event on the ListBoxItem? Even better, how do I bind it to ICommand.
I found some bits and pieces but don't know how to connect them. It seems InputBinding seems could help me or EventSetter.
Below is a simple example that handles Ctrl + PreviewMouseLeftButtonDown using an EventSetter in the ListBoxItem's Style. This is probably what you want.
XAML:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBoxItem_PreviewMouseLeftButtonDown"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<s:String>Item1</s:String>
<s:String>Item2</s:String>
<s:String>Item3</s:String>
<s:String>Item4</s:String>
</ListBox>
Code-Behind:
void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0)
{
Console.WriteLine((sender as ListBoxItem).Content.ToString());
e.Handled = false;
}
}
To bind it to an ICommand, you can use an attached behavior like the EventToCommand behavior discussed here.
EDIT:
To address your comment, handling the Click-event will be a bit tricky for the ListBoxItem because of two things: 1) The ListBoxItem doesn't have a click event, and 2) The ListBoxItem internally handles some of the MouseEvents. Anyway, I came up with a simulated, attached ClickEvent to make it work. See below. Hope it works.
public class AttachedEvents
{
private static readonly DependencyProperty IsTriggerEnabledProperty =
DependencyProperty.RegisterAttached("IsTriggerEnabled", typeof(bool), typeof(FrameworkElement), new FrameworkPropertyMetadata(false));
public static readonly RoutedEvent ClickEvent;
static AttachedEvents()
{
try
{
ClickEvent = EventManager.RegisterRoutedEvent("Click",
RoutingStrategy.Bubble,
typeof(RoutedEventHandler),
typeof(FrameworkElement));
}
catch (Exception ex)
{ }
}
private static void SetIsTriggerEnabled(FrameworkElement element, bool value)
{
if (element != null)
{
element.SetValue(IsTriggerEnabledProperty, value);
}
}
private static bool GetIsTriggerEnabled(FrameworkElement element)
{
return (element != null) ? (bool)element.GetValue(IsTriggerEnabledProperty) : false;
}
public static void AddClickHandler(DependencyObject o, RoutedEventHandler handler)
{
FrameworkElement element = (FrameworkElement)o;
element.AddHandler(ClickEvent, handler);
element.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonUp);
element.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonDown);
}
public static void RemoveClickHandler(DependencyObject o, RoutedEventHandler handler)
{
FrameworkElement element = (FrameworkElement)o;
element.RemoveHandler(ClickEvent, handler);
element.MouseLeftButtonUp -= new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonUp);
element.PreviewMouseLeftButtonDown -= new System.Windows.Input.MouseButtonEventHandler(SimulatedClick_MouseLeftButtonDown);
}
static void SimulatedClick_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
UpdateIsTriggerSet(element);
Mouse.Capture(element);
}
static void SimulatedClick_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
FrameworkElement element = (FrameworkElement)sender;
bool isTriggerSet = (bool)element.GetValue(IsTriggerEnabledProperty);
// update the trigger set flag
UpdateIsTriggerSet(element);
//release the mouse capture
Mouse.Capture(null);
// if trigger is set and we are still over the element then we fire the click event
if (isTriggerSet && IsMouseOver(element))
{
element.RaiseEvent(new RoutedEventArgs(ClickEvent, sender));
}
}
private static bool IsMouseOver(FrameworkElement element)
{
Point position = Mouse.PrimaryDevice.GetPosition(element);
if (((position.X >= 0.0) && (position.X <= element.ActualWidth)) && ((position.Y >= 0.0) && (position.Y <= element.ActualHeight)))
{
return true;
}
else
{
return false;
}
}
private static void UpdateIsTriggerSet(FrameworkElement element)
{
Point position = Mouse.PrimaryDevice.GetPosition(element);
if (((position.X >= 0.0) && (position.X <= element.ActualWidth)) && ((position.Y >= 0.0) && (position.Y <= element.ActualHeight)))
{
if (!(bool)element.GetValue(IsTriggerEnabledProperty))
{
element.SetValue(IsTriggerEnabledProperty, true);
}
}
else if ((bool)element.GetValue(IsTriggerEnabledProperty))
{
element.SetValue(IsTriggerEnabledProperty, false);
}
}
}
Sample usage is shown below. I can't seem to set the attached event in XAML (I'm not sure why) so I had to do a workaround here. What I do is wait 'til the ListBoxItem gets loaded and Attach the event handler in the code-behind.
XAML:
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<EventSetter Event="Loaded" Handler="OnLoaded"/>
</Style>
</ListBox.ItemContainerStyle>
...
</ListBox>
Code-Behind:
void OnLoaded(object sender, RoutedEventArgs e)
{
AttachedEvents.AddClickHandler((sender as ListBoxItem), HandleClick);
}
void HandleClick(object sender, RoutedEventArgs e)
{
if ((Keyboard.Modifiers & ModifierKeys.Control) > 0)
{
Console.WriteLine("Ctrl + Clicked!");
}
}