Is it possible to use an EventTrigger to set properties on another element?

Sep 13, 2010 at 3:44 PM

Hi,

I just started using WPF and this awesome framework.

Is it possible to change a property on another element in an EventTrigger?

<Button Content="Presentation">
	<i:Interaction.Triggers>
		<i:EventTrigger EventName="Click">
			<cal:ActionMessage MethodName="DoSomeAction" />
			<SomeSortOfSetter ElementName="MyOtherElement" Property="Enabled" Value="False" /> 
		</i:EventTrigger>
	</i:Interaction.Triggers>
</Button>

Is there something like 'SomeSortOfSetter'?

Thanks

Coordinator
Sep 13, 2010 at 5:31 PM

There is a way to do that, I think. But, I do not remember what it is called. Also, you should be careful about doing a lot of this..it can make it difficult to determine what is happening in the view. You may find that you can accomplish the same thing by using data binding and pushing this sort of logic into a view model.

Sep 13, 2010 at 5:37 PM

I don't know if such a setter does exist, so I can't help you on this. You could, however, search some resources about Windows.Interactivity, too, since Caliburn.Micro messaging infrastructure is build on it.

I also would like to suggest an alternative, more MVVM-oriented, approach.
I guess you want your screen to change its state after the (first) execution of DoSomeAction, disabling an element; you might consider expressing this state as an explicit property in your ViewModel (for example, IsOtherZoneActive), and setting the new state at the end of the DoSomeAction method. Then you might represent the VM state change, in the view side, adding a binding of MyOtherElement's Enabled property to VM's IsOtherZoneActive property.

This may seem an useless indirection, but this way you can extract all the code driving the UI in a testable "model of the view", thus freeing the view from any (hard to test) logic.

Hope it helps.

Sep 14, 2010 at 8:34 AM

I tried to generalize problem, maybe that was wrong of me. There could be just me doing something in the complete wrong way.

My actual problem is with a Popup element.

<Window x:Class="WpfPopupTest.MainWindow"
		xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
		xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		xmlns:cal="clr-namespace:Caliburn.Micro;assembly=Caliburn.Micro"
		xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
		Title="MainWindow" Height="350" Width="525">
	<Grid>
		<ToggleButton x:Name="MyToggleButton" Content="Click me" Width="100" Height="30" />
		<Popup StaysOpen="False" IsOpen="{Binding IsChecked, ElementName=MyToggleButton,Mode=TwoWay}" PlacementTarget="{Binding ElementName=MyToggleButton}" Placement="Bottom" >
			<StackPanel>
				<Button Content="Clicking me should perform the action and close the popup" Padding="15">
					<i:Interaction.Triggers>
						<i:EventTrigger EventName="Click">
							<cal:ActionMessage MethodName="DoSomeAction" />
							<!-- Close the Popup -->
						</i:EventTrigger>
					</i:Interaction.Triggers>
				</Button>
			</StackPanel>
		</Popup>		
	</Grid>
</Window>

I can solve my problem by using methods and properties on my ViewModel, but I though that there might be some way to accomplish this without involving the vm.

But maybe the correct MVVM-approach is to have the vm decide when the Popup should be shown?

Sep 14, 2010 at 10:34 AM

I ended up using marcoamendola's approach.

I added a boolean property (IsPopupVisible) to my ViewModel which I bound with <MyToggleButton IsChecked="{Binding IsPopupVisible,Mode=TwoWay}" ..> and then in DoSomeAction I set this property to false.

Thanks!

Sep 14, 2010 at 2:04 PM
Edited Sep 14, 2010 at 2:07 PM

ViewModel is definitely a UI side component, not a Domain one; so yes, I usually consider deciding if a panel or a popup is visible a VM concern, expecially if the VM is involved in the process at least once.
In your scenario, for example, VM wants the popup to be closed at the end of the action; there may also be more complex cases requiring popup to close only if some conditions are met.
You can end up with a very complex logic and it's better to have the opportunity to test it, or at least having it in just one class.

Obviously there's not a strict rule; for example the ComboBox is able to drive its popup autonomously, and there's no need to manage it through the VM: it's a view-only concern.

To avoid coupling too much the VM with its corresponding view, I usually try to use more "abstract" property name in the VM, focusing on the concept more than the visual representation.
That's why I suggested "IsOtherZoneActive" and usually avoid mentioning things like "Popup", "Expander", etc. in VM properties.
The actual controls used are only visual representation of the conceptual state of the VM, and it's very common having the same property represented differently (you may have a disabled panel instead of the popup, even in the same application).

Sep 15, 2010 at 6:32 AM

@marcoamendola, I really agree with your practice to use a conceptual name for a VM property instead of something that refers to the type or name of a UI control. Unfortunately, I run into way too many developers who are still doing that horribly wrong.

Sep 17, 2010 at 4:36 AM
Edited Sep 17, 2010 at 10:26 AM

I know you took a different approach, but expression blend samples has a SetDataProperty and SetProperty actions.

http://expressionblend.codeplex.com/wikipage?title=Behaviors%20and%20Effects&referringTitle=Documentation

I remember there was a problem in one of them, it may be fixed now. I created my own version based on these that work fine. Currently I'm using them in WPF 4 and Silverlight 4, I have an older version based on the old Blend Sdk's data helpers and later nRoute's BindableTriggerAction that let you bind trigger and action properties if attached to a FrameworkElement (Blend Trigger/Actions are DepedencyObject's and can't directly be binding targets in Silverlight 3, and I believe still so in Windows Phone 7).

 

The nRoute toolkit also has a SetPropertyAction, the toolkit plays fine and dandy with Caliburn as I'm currently using it:

http://www.orktane.com/Blog/post/2009/09/29/Introducing-nRouteToolkit-for-Silverlight-(Part-I).aspx