I have this problem in a bigger Project...... so I set up a 'Testpoject' as Proof of Concept:
New Silverlight-Application
Add Listbox
Fill listbox with a few Checkboxes
Register listBox1_MouseLeftButtonDown
register listBox1_MouseRightButtonDown
You will see, that the listBox1_MouseLeftButtonDown won't fire under any circumstances....
listBox1_MouseRightButtonDown however fires just fine.
I tried using a custom Class deriving from ListBox and overriding, assuming something in the ListBox Class was setting e.Handled = false, but this did not change the behaviour, either.
Any Ideas on why this happens and how to fix?
(This problem also stops the 'parent'-control from receiving the Click-Event... so the Event-passing is broke)
:edit:
I fixed my problem with a workaround... so an answer is not required anymore. Just if somebody feels like figuring out why this is happening for the sake of it ;)
This seems to answer your question. To quote:
That's because ListBoxItem internally handles this event as well as the MouseLeftButtonDown event (halting the bubbling) to implement item selection.
The solution is to add the event handler in the code-behind file. From the article:
Although setting the RoutedEventArgs parameter's Handled property to true in a routed event handler appears to stop the tunneling or bubbling, individual handlers further up or down the tree can opt to receive the events anyway! This can only be done from procedural code, using an overload of AddHandler that adds a Boolean handledEventsToo parameter.
See the caveat at the end though.
This is by design. If you check the framework code, you'll see that the ListBoxItem sets the Handled property to true.
I had this same exact problem, so in my ListBoxItem.ItemTemplate, I added the event handler in my content.
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" MouseLeftButtonDown="StackPanel_MouseLeftButtonDown">
... other controls ...
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Related
I am studying WPF. Infollowing the example, there is something I don't understand.
In the example below, tunneling event handlers are called, but I am curious about the reason why all bubbling event handlers are not called.
<ScrollViewer PreviewMouseWheel="ScrollViewer_PreviewMouseWheel" <!-- called -->
MouseWheel="ScrollViewer_MouseWheel"> <!-- not called -->
<StackPanel>
<ListBox
PreviewMouseWheel="ListBox_PreviewMouseWheel" <!-- called -->
MouseWheel="ListBox_MouseWheel" <!-- not called -->
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"/>
</StackPanel>
</ScrollViewer>
Thank you.
A ListBox contains a ScrollViewer that handles the MouseWheel event in order to scroll its content by setting e.Handled = true in the corresponding event args. Event handlers up the chain are not called then. This also applies to other variants of items controls that feature a scroll viewer like ListView or DataGrid.
When you set the value of the Handled property to true in the event data for a routed event, this is referred to as "marking the event handled".
[...] handlers added in XAML or the common signature of AddHandler are not invoked in response to a routed event where the event data is already marked handled.
However, at least when adding routed event handlers in code, you can also use handled events.
You must go through the extra effort of adding a handler with the handledEventsToo parameter version (AddHandler(RoutedEvent, Delegate, Boolean)) in order to handle routed events that are marked handled by earlier participants in the event route.
For example, you can define an x:Name for your ListView to be able to access it in code-behind.
<ListBox x:Name="MyListBox"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"/>
Then you can register the event e.g. in the constructor. The last parameter of the AddHandler method is set to true, so that already handled events will still invoke the registered event handler.
MyListBox.AddHandler(MouseWheelEvent, (MouseWheelEventHandler)ListBox_MouseWheel, true);
Alternatively, you can use the PreviewMouseWheel event instead if it fits your requirements.
I have a simple WPF Control with a button on it that uses caliburn micro event wiring (caliburn cheat sheet):
<UserControl xmlns:cal="http://www.caliburnproject.org">
<Button Name="Save" Content="Save"
cal:Message.Attach="[Event MouseEnter] = [Action DoSmthg]" />
</UserControl>
The user control is integrated to the main view as
<ContentControl cal:View.Model="{Binding MyViewModel}" />
In the view model I have the corresponding method:
public void DoSmthg()
When I start my app, the method is not called, so I initial considered that I made a mistake with the wiring. But when I modify the XAML file while debugging (e.g. adding a space), everything works as expected. SO it looks to me, that while startup, the event is overridden by something else, and when I update the XAML the caliburn event is updated again. Since I removed everything that could disturb in my code I have no idea what the reason could be. Any ideas, how I can find the issue?
The issue is independent of the event type. Also happens when I add parameters. And no errors or warnings in output.
Remark: When I add a breakpoint in the OnInitialized(EventArgs e) method of the code behind and execute Message.GetAttach(Save) I can see that the event is set up correctly.
I have a ComboBox which uses ListBox to show items. ListBox’s ItemSource is bound with a CollectionViewSource.
Issue: Once I open ComboBox and scroll through the items and leave it in middle or at bottom. Once I reopen ComboBox, even though I refresh or reload ItemSource (CollectionViewSource), the Scrollbar remains at the same place where I left it last time. I want it to be as default (at top) each and every time I reload ItemSource.
Is there is any way of doing this in XAML itself? Another thing, I cannot use Behavior or Attach property. I want any template or style for this.
In order for a Style on a ListBox to embody some behavior for the <ScrollViewer>, you would need to use an Attached Property / Attached Behavior to control the ScrollViewer's "grabber" position. This is because your collection is bound to your ListBox and notifying when it is updated needs to drive a behavior that isn't natively on the ListBox. It may be possible to reset the scroll position with a <ControlTemplate> for the <ScrollViewer> itself, but I imagine it would be difficult as it would likely involve manipulating Transforms / StoryBoards based on DataTriggering with your ItemsSource, but again that may cause a dependency on needing to use an Attached property, which for some reason you can't use...
If you simply want to get a result now, and you don't care about testability or re-usability, I would handle the TargetUpdated event in the code-behind. It's ultimately what the Attached Behavior would end up doing. On the other hand, if you do care about re-usability then you need to evaluate and challenge why you can't use an Attached Behavior (they are also testable, too); an Attached Behavior would be also easier than trying to edit a ControlTemplate.
Here is the code-behind approach:
.xaml:
<ListBox x:Name="myListBox"
ItemsSource="{Binding MyItemsSource, NotifyOnTargetUpdated=True}"
TargetUpdated="ListBox_TargetUpdated"/>
.xaml.cs:
private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
if (myListBox.Items.Count > 0)
myListBox.ScrollIntoView(myListBox.Items[0]);
}
Edit: On the flip-side, if you are using MVVM, you can do something like this SO post suggests and set IsSynchronizationWithCurrentItem="True" and when you refresh your ItemsSource, simply set your SelectedItem to the first in the list and handle the SelectionChanged event in your vm.
Just this. I want that when the databound property Text of a TextBlock for example changes, an animation is performed to give some feedback to the user. How can this be done?? thanks!
I found a way to do it using a PropertyChangedTrigger
<Interactivity:Interaction.Triggers>
<ec:PropertyChangedTrigger Binding="{Binding KnownMeaning}" >
<eim:ControlStoryboardAction Storyboard="StaticResource Storyboard2}"/>
</ec:PropertyChangedTrigger>
</Interactivity:Interaction.Triggers>
There may be a more elegant solution, but I think this is pretty straightforward
You can simply add event to ViewModel and start animation (Storyboard.Begin) in code-behind. Or even do it without extra event by adding handler of ViewModel.PropertyChanged in code-behind.
More advanced options are listed here:
Re: How to Annimate (Storyboard.Begin() ) in MVVM.
I want to listen for changes to the IsEnabled property on WPF elements (so that I can run some common code whenever it changes) without having to put a load of code in each window/page.
I also want to avoid any potential problems with memory leaks due to strong event listeners. I've come across some articles that suggest using weak event listeners etc. but that seems awfully complicated for something that seems like it should be really easy.
I don't want to have to subclass controls in order to do this as there are several control types (and probably more in future) that I want to listen for the IsEnabled change on.
Has anyone come up with a neater way of handling this?
A neat solution to this problem would to create a custom attached property and setup a one way binding with the IsEnabled property as the source:
<Control IsEnabled={Binding IsEnabledProperty}
AttachedProperty={Binding RelativeSource={RelativeSource Self}, Path=IsEnabled, Mode=OneWay}"/>
This allows you to handle the common functionality in the attached property's changed handler (which could involve firing a custom routed event as IsEnabled is a regular CLR event and won't bubble up).
There is a Control.IsEnabledChanged event.