I have a custom control inherited from UserControl that I am enabling/disabling via a binding and trying to use an EventTrigger for IsEnabledChanged to cause a ChangePropertyAction behavior to execute.
<local:StockmarketFilecard x:Name="StockmarketReport2" VerticalAlignment="Bottom" Panel.ZIndex="0" IsEnabled="{Binding DataContext.BankReportEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<Interactions:Interaction.Triggers>
<Interactions:EventTrigger EventName="IsEnabledChanged">
<Interactions:ChangePropertyAction PropertyName="MaxHeight" Value="100"/>
</Interactions:EventTrigger>
</Interactions:Interaction.Triggers>
</local:StockmarketFilecard>
Interactions uses the http://schemas.microsoft.com/xaml/behaviors namespace.
Problem is that the ChangePropertyAction is not executed despite the control visibly becoming enabled/disabled. I have tested adding a code-behind eventhandler for IsEnabledChanged on the control and it is called as expected. I have also tested triggering on the Loaded event to verify that the action is correct and the control changes as expected.
What am I missing to get the EventTrigger to trigger on IsEnabledChanged?
What am I missing to get the EventTrigger to trigger on IsEnabledChanged?
The fact that the EventTrigger only handles routed events and IsEnabledChanged is not a routed event.
You may either set the MaxHeight property in an event handler in the code-behind of the view, or implement an attached behaviour as suggested here.
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.
in my window I want to use this code
<i:Interaction.Triggers>
<i:EventTrigger EventName="SourceInitialized">
<command:EventToCommand Command="{x:Static wpf:Window.InitializeWindowProcessHookCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
to hook the SourceInitialized event to a command on my so-called Window class.
I am using MvvMLight EventToCommand and if it works perfectly if I use the Loaded event instead of the SourceInitialized, so we can assume that the command and further logic is working.
Additionally, using the event with code behind works, but I am looking for a solution using EventTriggers (if possible).
When looking up a possible solution, I stumbled across a topic on MSDN, which is about this exact topic, and the OP points states that he successfully bound to the Loaded event, but cannot get binding to the SourceInitialized to work
[and I] want to write a similar one for windows source initialized event, but find that Window.SourceInitializedEvent is not exposed
Is there any possible solution to that?
Many thanks in regard
The SourceInitialized event fires before your trigger has a chance to invoke the command so this won't work.
Also, it doesn't make much sense to fire the command using an EventTrigger that is defined in the XAML markup just for the sake of not having to do it from the code-behind of the same view. MVVM is not about eliminating view-related code from the views and it doesn't break the pattern to invoke the command from the code-behind of the same view as your XAML markup is defined in.
So invoke the command from an event handler in the code-behind or subscribe to another event.
As you have already noticed, there is no way to handle the SourceInitialized event of a window using an EventTrigger that is defined in the XAML markup of the same window.
I have a ListBox with a two way binding from my View Model to SelectedItem. When the View Model is updated, SelectedItem is updated and the SelectionChanged event is fired. SelectionChanged is also fired through direct user interaction in the UI e.g. clicking with mouse, touch selection, keyboard input.
I would like to perform a certain action in SelectionChanged only if the change came from UI interaction.
There appears to be properties in SelectionChangedEventArgs that cater to this (e.g. OriginalSource and UserInitiated) but they are not different between the two cases.
Is distinguishing between the two possible?
You can use SourceUpdated and TargetUpdated events. If you set Binding's NotifyOnSourceUpdated and NotifyOnTargetUpdated properties to True, then you can handle those events to distinguish the source of the change:
<ListBox ItemsSource="{Binding TestCollection}"
SelectedItem="{Binding TestSelection, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"
SourceUpdated="ListBox_SourceUpdated" TargetUpdated="ListBox_TargetUpdated"
SelectionChanged="ListBox_SelectionChanged" />
Those events are called before SelectionChanged so you either can take action in their handlers or set some flag to use in SelectionChanged or whatever.
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>
I have a control template with a toggle button. This ToggleButton has it's IsChecked property one way bound to a dependancy property. If i set the dependancy property explicitly the binding works.
The problem is that after I interact with the toggle button in the UI, the bindings don't update the IsChecked property if I set the dependency property explicitly.
I do have a work arround using TwoWay binding which works fine. My question is, why does it behave this way? Am I missing something? Is there a bug in the binding mechanism of Silverlight?
EDIT TO INCLUDE SNIPPET:
The binding in the ControlTemplate looks something like (could be replaced with TemplateBinding)
<ToggleButton x:Name="PlayPause" Grid.Column="0"
IsChecked="{Binding Paused, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Center"
Width="50" Height="50"/>
The explicit setting of the dependency property is the fairly bog standard:
myComponent.Paused = true;
WPF deletes one way bindings when the target property (IsChecked in this case) is modified. Silverlight used to keep the binding when IsChecked was modified. If Paused was later set, this value would overwrite IsChecked as well.
According to you, it seems Silverlight reverted to WPF behavior. Oh well. Personally, I consider modifying a binded property a bug. If the properties are not meant to be in sync commanding may be a better solution.
You should use TwoWay binding
Make sure that the object that contains your Paused property supports INotifyPropertyChanged.
Make sure that the setter for Paused triggers the PropertyChanged event