none
Can Triggers affect elements they are not applied to?

    Question

  • I have this program here that consists of a Label and a Button.  I have a Style that contains a trigger that says that when someone mouse overs the button the button background should turn yellow.  But let's say I wanted the label to also turn yellow when someone mouses over the button.  How can I have a Trigger applied to one element (in this case, the button) affect other elements around it?  I know you can do this in code, but how can this be accomplished in XAML?

     

    <Window x:Class="WindowsApplication3.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="WindowsApplication3" Height="300" Width="300"

    >

    <Window.Resources>

    <Style TargetType="{x:Type Button}" x:Key="yellowButton">

    <Style.Triggers>

    <Trigger Property="IsMouseOver" Value="True">

    <Setter Property="Background" Value="Yellow" />

    </Trigger>

    </Style.Triggers>

    </Style>

    </Window.Resources>

    <StackPanel>

    <Label Margin="10">My Label</Label>

    <Button Width="150" Height="100" Style="{StaticResource yellowButton}">My Button</Button>

    </StackPanel>

    </Window>

     

    -Ryan

    Monday, July 17, 2006 3:44 PM

Answers

  • Setting the TargetName should work, however it cannot work in a style. If you think about it, it makes sense: if you set the target name in the style in what scope should the runtime look for the name?
    So you could try to set target name but in the triggers of the element itself, not in the style.
    Monday, July 17, 2006 6:19 PM

All replies

  • something like this would work

    <StackPanel>

    <Label Margin="10" x:Name="lbl">My Label</Label>

    <Button Width="150" Height="100" Style="{StaticResource yellowButton}" x:Name="btn1">My Button

    </Button>

    <StackPanel.Triggers>

    <EventTrigger RoutedEvent="Button.MouseMove" SourceName="btn1">

    <EventTrigger.Actions>

    <BeginStoryboard>

    <Storyboard>

    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="lbl" Storyboard.TargetProperty="(Label.Background)">

    <DiscreteObjectKeyFrame KeyTime="0:0:0">

    <DiscreteObjectKeyFrame.Value>

    <SolidColorBrush Color="Yellow"/>

    </DiscreteObjectKeyFrame.Value>

    </DiscreteObjectKeyFrame>

    </ObjectAnimationUsingKeyFrames>

    </Storyboard>

    </BeginStoryboard>

    </EventTrigger.Actions>

    </EventTrigger>

    <EventTrigger RoutedEvent="Button.MouseLeave" SourceName="btn1">

    <EventTrigger.Actions>

    <BeginStoryboard>

    <Storyboard>

    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="lbl" Storyboard.TargetProperty="(Label.Background)">

    <DiscreteObjectKeyFrame KeyTime="0:0:0">

    <DiscreteObjectKeyFrame.Value>

    <SolidColorBrush Color="White"/>

    </DiscreteObjectKeyFrame.Value>

    </DiscreteObjectKeyFrame>

    </ObjectAnimationUsingKeyFrames>

    </Storyboard>

    </BeginStoryboard>

    </EventTrigger.Actions>

    </EventTrigger>

    </StackPanel.Triggers>

    </StackPanel>

    Monday, July 17, 2006 4:08 PM
  • More simple than that, a Setter can have a TargetName. So give your Label a Name and define two setters, one that sets your button and the other that sets your Label by using the TargetName property.

     

    <Setter TargetName="MyLabel" Property="Background" Value="Yellow"/>
    <Setter Property="Background" Value="Yellow"/>

     

    That should do what you want.

    Monday, July 17, 2006 6:01 PM
  • Lee d

    That does work but it's more complicated.  Is there a way to do this with a Trigger rather than with an EventTrigger so that you can work with Properties instead of with events, storyboards, and animations?  Also with this method the values have to be manually reset, rather than resetting automatically as they do with you use a Trigger.  I'm just trying to figure out all this trigger stuff.  It seems forcing people to create animations and keyframes just to change a property value can't be the right way to do this.

    -Ryan

     

     

    Monday, July 17, 2006 6:02 PM
  • <Setter TargetName="MyLabel" Property="Background" Value="Yellow"/>
    <Setter Property="Background" Value="Yellow"/>

    That doesn't compile.  It gives the error 'TargetName property cannot be set on a Style Setter'

    Monday, July 17, 2006 6:04 PM
  • Oh I'm sorry...try putting your trigger on Window.Triggers instead of Style.Triggers.

    <Window x:Class="WindowsApplication3.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="WindowsApplication3" Height="300" Width="300"

    >

    <Window.Triggers>

    <Trigger SourceName="MyButton" Property="IsMouseOver" Value="True">

    <Setter Property="Background" Value="Yellow" />
    <Setter TargetName="MyLabel"  Property="Background" Value="Yellow" />

    </Trigger>

    </Window.Triggers>

    <StackPanel>

    <Label x:Name="MyLabel" Margin="10">My Label</Label>

    <Button x:Name="MyButton" Width="150" Height="100" >My Button</Button>

    </StackPanel>

    </Window>

    Monday, July 17, 2006 6:19 PM
  • Setting the TargetName should work, however it cannot work in a style. If you think about it, it makes sense: if you set the target name in the style in what scope should the runtime look for the name?
    So you could try to set target name but in the triggers of the element itself, not in the style.
    Monday, July 17, 2006 6:19 PM
  • Hmmm...still doesn't like it....
    Monday, July 17, 2006 6:26 PM
  • Looks like we're in a catch-22.

     MSDN wrote:

    Note that the collection of triggers established on an element only supports event triggers, not property triggers. If you require property triggers, you must place these within a style and then assign that style to the element either directly via the Style property, or indirectly through type-based style reference.

    Elements only support event triggers. Styles don't support targetname. What to do?

    Monday, July 17, 2006 6:30 PM
  • TargetName can only be used in ControlTemplate.Triggers it appears.
    Monday, July 17, 2006 6:49 PM
  • It is easier to do this in code than XAML
    Monday, July 17, 2006 6:53 PM
  • Definitely...definitely easier in code. Unless you were to define a custom control for this grouping. Assuming that it even merits being its own control.
    Monday, July 17, 2006 7:04 PM
  • TargetName in Template.Triggers is restricted to the subset of child objects within the Template.  This is imposed as an encapsulation boundary.  Same with how a Style can only affect the properties on the object being styled.

    TargetName outside Template.Triggers are unbounded and unstructured - if any element can dictate property values of any other element, you start weaving a complex web of dependencies.  Or even worse, the occasional infinite loop.  (There are implementation challenges as well, but we don't need to get into that here.)

    Think of all the "Programming 101" lessons about why the "GOTO" command is bad in a programming language.  GOTO exists because it is necessary in certain scenarios, but most language purists would rather rip them out because they're so easily abused.

    Yes - you can do this from C# code.  You can also use "goto" in C#, but that doesn't mean either of them are necessarily good ideas.

    These are powerful tools... and they can be used for good or evil.  Take care in wielding your powers.  [Insert Dark Side of the Force joke here.]

    Monday, July 17, 2006 9:15 PM
  • But in the elements Triggers you cannot add anything but an EventTrigger. What if you want to use a plain old property based Trigger?

    I find myself running in circles trying to uses triggers in WPF because of these types of situations where one trigger can be used here but not there and usually it stems from wanting to use triggers on one element to affect properties on another.

    This blog post is a great example of the exact frustrations that I have with using Triggers in the capacity that I described above.

    http://flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!341.entry
    • Edited by kainhart Wednesday, February 03, 2010 5:44 PM Added a informative link
    Wednesday, February 03, 2010 5:39 PM
  • I was in the same predicament and my colleague suggested another way.  Instead of using a trigger on the property that is changing, you can use a binding on the property that you want to change.  This is the property for the element that you wanted to use in the TargetName.  You would be binding to the the property of the element that you want your value to react to.  You may need to create a custom Converter to convert from the value you depend on to the value that the property accepts.

     

    Friday, July 23, 2010 12:37 AM
  • lidiya, this is a good solution. Unfortunately in my case I am trying to get rid of bindings and switch to triggers for performance reasons. I'm not sure but it seems to me triggers should be more efficient than tying into the binding engine, however I can't even come up with an implementation to test this theory!

    This is such a mess I'm so frustrated that everything I try is blocked by WPF. Is there a logical reason why we can't use Triggers in the Triggers collection of an element?? How absurd it all is.

    Oh and guess what else? So we decide to just go ahead and use an EventTrigger.... well do you think we can just define a Setter in the EventTrigger? No! You can only use animations! So now we have to define an entire animation with a key frame at time 00:00:00 just to set a value! I just don't understand why!
    Wednesday, July 28, 2010 5:10 PM
  • I'm new to WPF, well new, I read McDonalds Pro WPF book almost cover to cover (I never generally do this !) and have been coding WPF screens now for a few months.  This has taken a while and I still feel like I'm just getting to a moderate level of expertise.

    BUT 1) WPF is a revelation to me having been developing screens now since 1980 from  z80a assembly level screen work through TUI's, visual C++ and active x (never again) through to a technology  that can truly bring me a NUI interface (Yeah, I'd never heard of this before, its a Natural User Interface).  MVVM style WPF screens bring me so, so much more than I ever dreamt I could do before.

    AND 2) its mature, but still needs some work and all the points you bring above are included in this.  Its pretty polished and I'm yet to find many bugs (any ?) but debugging needs to be vastly improved (if an exception is thrown I want to track this back to the xaml easily through the stack trace).

    Wednesday, December 19, 2012 11:36 PM