How do I make multiple event bindings with ImpromptuInterface.MVVM? - wpf

Binding to a single event with ImpromptuInterface.MVVM is very simple:
<DataGrid MVVM:Event.Bind="{Binding Events.RowEditEnding.To[DoRowEditEnding]}"
...
How do I bind to a second event in the same DataGrid control?

You can always chain another event after the To statement. Here is an example where I've used it to bind multiple mouse listening events from one object:
<Grid Background="{Binding Color}" MVVM:Event.Bind="{Binding Events.MouseEnter.To[CenterMouseEnter].MouseLeave.To[MouseLeave].MouseLeftButtonDown.To[LeftButtonClick]}"

Related

Bubbling event is not called

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.

EventTrigger fails to trigger on IsEnabledChanged

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.

How can I tell if a SelectionChanged event was a result of UI interaction or programmatic change in the ViewModel in WPF?

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.

WPF Create user control dynamically on change of observablecollection OR Visualize nested ObservableCollections by using nested User controls

I am new to WPF, and I can't find a solution to my problem.
I've got an Observable Collection in my ViewModel:
ObservableCollection<Process>
Within the Process class is another ObervableCollection:
ObservableCollection<BurstTime>
I want to add a new User control (what can vizualize the actual state of Process data, and it's burst times) dynamically to one of my StackPanel in my view each time, when a new Process is activated, and remove it when the process is ended (when the observable collection changes).. During it's run, I want to diplay it's Burst Time collection, what can change in time..
I've tried to subscribe to the CollectionChanged event in my user control's codebehind and use a User Control inside another User Control and dynamicalli create required TextBlocks when my event handler runs, but I always get a System.Reflection.TargetInvocationException (inner exception: System.NullReferenceException) when my outer InitializeComponent() is running..
public ResourceUseView()
{
InitializeComponent();
((ObservableCollection<Process>)DataContext).CollectionChanged += ResourceUseView_CollectionChanged;
}
Is it possible to do it? Is it possible to create or remove User controls in my outer UserControl's codebehind while reacting to ObservableCollection's element changes, and update the outer when an element changes in the inner ObservableCollection? Are there other ways than instantiate a UserControl inside another User Control dynamically? Is there any special control, what can display ObservableCollection in ObservableCollection?
(If it'is possible, I would like to have different user controls as much as elements my ObservableCollection have..)
Thanks for your help!
The constructor runs before the data binding, so you are probably seeing that error because the DataContext is null at the time the constructor runes.
To bind to a collection, use a control that contains an ItemsSource property. And if you want to bind to a nested collection, modify the ItemTemplate of the control to use another control with an ItemsSource property for the nested collection.
Here's an example using an ItemsControl
<ItemsControl ItemsSource="{Binding Processes}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ItemsControl ItemsSource="{Binding BurstTimes}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
I have some examples of the ItemsControl on my blog if you're interested.
If you want to specify a UserControl for each object, then set the ItemTemplate for the first ItemsControl to be the ProcessUserControl, and inside the ProcessUserControl have an ItemsControl bound to BurstTimes and use a BurstTimeUserControl for it's ItemTemplate
<ItemsControl ItemsSource="{Binding Processes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ProcessUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
<UserControl x:Class="MyNamespace.ProcessUserControl ...>
...
<ItemsControl ItemsSource="{Binding BurstTimes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:BurstTimeUserControl />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
...
</UserControl>

How to listen Binding.SourceUpdated on all children of a root element?

I want to listen Binding.SourceUpdated on all the child bindings defined.
http://msdn.microsoft.com/en-us/library/system.windows.data.binding.sourceupdated.aspx says
Set the NotifyOnTargetUpdated or NotifyOnSourceUpdated property (or both) to true in the binding. The handler you provide to listen for this event must be attached directly to the element where you want to be informed of changes, or to the overall data context if you want to be aware that anything in the context has changed.
That means we should be able to listen those event per DataContext instead of per Binding element.
Like most WPF events, SourceUpdated is a Routed Event. Any event handler for this event placed on a given element will also be called when a child element raises this event.
If you have the following code:
<StackPanel Binding.SourceUpdated="OnBindingSourceUpdated">
<TextBlock Text="{Binding Path=A, NotifyOnSourceUpdated=True}" />
<TextBlock Text="{Binding Path=B, NotifyOnSourceUpdated=True}" />
</StackPanel>
The handler OnBindingSourceUpdated will handle binding source changes for both textboxes. Place the attached event handler on the element where the data context is originally defined and you'll get notifications for every source change.

Resources