Conditional execution of EventTriggers in Silverlight 3 - silverlight

I'm currently working on the UI of a Silverlight application and need to be able to change the visual state of a control to one of two possible states based on it's current state when handling the same event trigger.
For example:
I have a control that sits partially in a clipping path, when I click the visible part of the control I want to change the state to "visible" and if I click it again when it is in its "visible" state I want to change to the "hidden" state.
Example XAML:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<ic:GoToStateAction StateName="Visible"/>
<ic:GoToStateAction StateName="Hidden"/>
</i:EventTrigger>
</i:Interaction.Triggers>
Where "i" is "System.Windows.Interactivity;assembly=System.Windows.Interactivity" and "ic" is "Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions". I'm currently working in Expression Blend 3 and would prefer to have a XAML only solution but am not opposed to coding this if it is completely necessary. I have tried recording a change in the target state name in Blend but this did not work.
Any thoughts on this?

If you have only 2 states the easiest would be to simply call GoToNextState to rotate between states. E.g.:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<si:GoToNextState/>
</i:EventTrigger>
</i:Interaction.Triggers>
If you have other states as well, then:
add a property on the underlying viewmodel IsVisible
have a trigger that would call a method (using CallMethod action) that would toggle that property om MouseLeftButtonUp
have a DataStateBehavior bound to IsVisible property
E.g. something like this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonUp">
<si:CallDataMethod Method='ToggleIsVisible'/>
</i:EventTrigger>
</i:Interaction.Triggers>
<i:Interaction.Behaviors>
<si:DataStateBehavior Binding='{Binding IsVisible}' Value='True' TrueState='Visible' FalseState='Hidden'/>
</i:Interaction.Behaviors>

In the end I achieved this by creating a simple custom action called ToggleStateAction to encapsulate this behaviour for me.

Related

WPF how to write a trigger to invoke command action or set property based on condition

I would like to add trigger on mouse left click on my control. On trigger, if particular property of view model is set, then set some control property. However if viewmodel property is not set, invoke some command action. Is it achievable in WPF?
Currently I only have
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseRightButtonDown">
<interactivity:InvokeCommandAction AutoEnable="False"
Command="{Binding SomeCommand}"
CommandParameter="{x:Static SomeParameter" />
</i:EventTrigger>
</i:Interaction.Triggers>
I am not sure how to check a condition and based on result either invoke command or set control property.
Please suggest.

WPF: How to bind MouseEnter event from an ItemsControl.ItemTemplate to elements outside this ItemsControl

I'm new to WPF but I manage to advance slowly towards writing my first serious project. I'm not sure I use the correct terms so please bear with me.
I implemented a set of ItemsControl (User Controls). The item source is a collection of items. Each item holds much data including it's own ID. I would like the User Control to change a property when the mouse hovers over another control outside this set of ItemsControl.
To complicate things the other control is also an element of another set of ItemsControl.
I already managed to store the ID of the hovered-over control in a top-level property (In the top DataContext) but I can't find a way to bind to it from within the element buried inside the DataTemplate.
Here's a screenshot:
In this example the user hovers over channel 14 - as a result, bottom axes X and Z should highlight (I chose them arbitrarily - according to data stored in the database).
I'd be grateful for any idea. Examples will be most welcome!
Assuming that:
1) You are using the MVVM design pattern.
2) That there is an underlying ViewModel for each of the items in your ItemsControl.
Then what you need to do is handle the MouseEnter and MouseLeave events in your view and have them trigger Commands in your view model. Then have those ViewModels update properties in your other ViewModels to have them highlight the appropriate items in the other ItemsControl.
<UserControl x:Class="ClassName"
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"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4">
<i:Interaction.Triggers>
<!-- this will call the associated commands on your viewmodel when the mouse enters/leaves the corresponding view in your itemscontrol, from there you can create the viewmodel functionality you'd like-->
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding MouseEnterCommand}"/>
</i:EventTrigger>
<i:EventTrigger EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding MouseLeaveCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid Height="10" Width="10">
<!--the content of your usercontrol-->
</Grid>
</UserControl>
Once you get the correct commands notifying your viewmodel that the mouse is hovering over it (or left it). You can manage the state of your viewmodels to create the affects you are looking for.
I really like the Blend SDK for stuff like this.
Since you mentioned you have the hovered-over ID setting in a property, you could start with a PropertyChangedTrigger at the root level. Next, you will probably want to invoke a command (rather than just a method), since your action includes a parameter (the ID). Use InvokeCommandAction for this. You can trigger a command either on the view or the view-model. If you want to trigger it on the view, then you'll probably want to use ElementName as the binding.
Here's an example.
<UserControl>
<i:Interaction.Triggers>
<!-- When "SelectedID" changes, invoke the "SelectedIDChangedCommand" on the
element "AxesAndButtons". Pass the value of "SelectedID" as the
command parameter -->
<ei:PropertyChangedTrigger Binding="{Binding SelectedID}">
<i:InvokeCommandAction CommandParameter="{Binding SelectedID}"
Command="{Binding ElementName=AxesAndButtons,Path=SelectedIDChangedCommand}" />
</ei:PropertyChangedTrigger>
<i:Interaction.Triggers>
<my:AxesAndButtonsControl x:Name="AxesAndButtons">
</my:AxesAndButtonsControl>
</UserControl>
I have assumed that ID property that gets changed is called "SelectedID" (and is a property of the root data context). Also, that your target user control has a defined ICommand "SelectedIDChangedCommand" dependency property that performs the update. That is, something like this:
public class AxesAndButtonsControl : UserControl
{
public static readonly DependencyProperty SelectedIDChangedCommand = DependencyProperty.Register("SelectedIDChangedCommand",
typeof(ICommand),
typeof(AxesAndButtonsControl),
new PropertyMetadata(null));
public AxesAndButtonsControl()
{
SelectedIDChangedCommand = new DelegateCommand(id => {
// highlight "X" and "Z" or whatever
});
}
}
Edit I just noticed that maybe you haven't bound the MouseOver event to update the SelectedID property yet. If that's the case, then you should be able to use an EventTrigger along with a ChangePropertyAction (in a FindAncestor or ElementName binding) to update it. Something like this:
<DataTemplate>
<Grid>
<i:Interaction.Triggers>
<!-- When "MouseEnter" gets triggered, set the property "SelectedID" on the
data context of the closest "UserControl" parent to the value of "ItemID"
in the current data context -->
<i:EventTrigger EventName="MouseEnter">
<ei:ChangePropertyAction Value="{Binding ItemID}"
TargetObject="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=DataContext}"
PropertyName="SelectedID" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</DataTemplate>

ComboBox's SelectionChanged inside datagrid not firing mvvm

I am using MvvmLight toolkit for my event interactions, i have tried many solutions available but none worked. The combo box inside my data grid's Selection Changed event is not firing
Here is my xaml:
i used both InvokeCommandAction and EventToCommand:
EventToCommand Command="{Binding rlcCbSelectionChanged, Mode=OneWay}"
PassEventArgsToCommand="True"
InvokeCommandAction Command="{Binding rlcCbSelectionChanged, Mode=OneWay}"
Please tell me what am i missing??
Selection Changed event successfully fires on data grid with same procedure given above.
Well answer is quite simple i was missing an attribute that is optional that is why i leave this one previously but that causes selection change event not to fire. So i add my view model key as a static resource and it worked :)
So my working xaml look like :
<i:EventTrigger EventName="SelectionChanged">
<i1:InvokeCommandAction Command="{Binding Path=rlcCbSelectionChanged, Source={StaticResource dvm}}"/>
</i:EventTrigger>

Silverlight 4 EventTrigger Handled

I have two nested Grid (FrameworkElement) items in my application.
<UserControl xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity">
<Grid x:name="OuterGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction x:Name="TheOuterCommand" Command="{Binding OuterCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid x:name="InnerGrid">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction x:Name="TheInnerCommand" Command="{Binding InnerCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Grid>
</Grid>
</UserControl>
Each of the InvokeCommands is attached to a DelegateCommand (from the Prism libraries) in the viewmodel.
OuterCommand = new DelegateCommand(OuterCommandMethod, e => true);
InnerCommand = new DelegateCommand(InnerCommandMethod, e => true);
At the moment, the EventTrigger on InnerGrid also triggers the event on the OuterGrid due to the MouseLeftButtonEvent not being handled at the InnerGrid level.
Is there a way I can notify the EventTrigger that it is handled and it should not bubble up to the OuterGrid?
At the moment, all I can think to do is have a wrapper FrameworkElement around the InnerGrid that uses an event on the XAML code-behind to set the event to handled. Does anyone have any other ideas?
---- Edit ----
In the end, I have included MVVM Light in my application and replaced InvokeCommandAction with RelayCommand. This is now working as I intended. I'll mark Bryant's answer as the winner for giving me the suggestion.
We have extended EventTrigger by adding dependency property called IsInner and then we always set a static flag in the inner EventTrigger. The outer EventTrigger unsets the flag and returns if the flag was set. That is extremely easy to write and works well.
Your best bet would be to pass the event args to the Command and then mark the event handled using the event args. You can do this by following this example here.

How can I bind an Event to changing the property on another element in the tree in XAML WPF?

I'll start off with the code as it should be fairly self-explanatory:
<commonControls:SearchTextBox
x:Name="searchTextBox"
Margin="6,0"
HorizontalAlignment="Right"
MinWidth="50"
Width="130"
SearchMode="Instant"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Search">
<cmd:EventToCommand Command="{Binding Search}"
CommandParameter="{Binding ElementName=searchTextBox, Path=Text}" />
</i:EventTrigger>
<i:EventTrigger EventName="Cancel">
<!-- Code to set searchTextBox.Text to Empty -->
</i:EventTrigger>
</i:Interaction.Triggers>
</commonControls:SearchTextBox>
The SearchTextBox has an Event called Cancel which executes if the user clicks the X button on the right hand side of the TextBox. What I would like to do is hook onto that event, and clear the Text property of the TextBox. I'm looking for a way to do this purely in XAML, with no Code Behind, and without hitting the ViewModel.
You can achieve this using a concept called 'attached behaviours', attached properties that on attachment handle events on the target object and perform some action as a result. See the following article:
http://www.codeproject.com/KB/WPF/AttachedBehaviors.aspx
I see you are already using blend interactions. Blend provides a simple framework for behaviours making them easier to implement, you simple override the OnAttach method to add your logic. See the example here:
http://www.scottlogic.co.uk/blog/colin/2011/06/metro-in-motion-part-7-panorama-prettiness-and-opacity/

Resources