相关文章推荐
豪爽的烈马  ·  Fragment思考_android ...·  12 月前    · 
欢乐的汽水  ·  CentsOS6 Tomcat7 ...·  1 年前    · 
着急的毛巾  ·  c# - (WPF) How to ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I've created a UserControl, similar to the following:

<UserControl>
    <StackPanel Orientation="Vertical">
        <StackPanel x:Name="Launch" Orientation="Horizontal" Visibility="Collapsed">
            <!-- Children here -->
        </StackPanel>
        <ToggleButton x:Name="ToggleLaunch" IsChecked="False" Content="Launch"/>
    </StackPanel>
</UserControl>

I've been trying to use a DataTrigger to make the 'Launch' StackPanel become visible when the ToggleButton is checked, and remain collapsed otherwise. However, at runtime I get an error stating "Failed object initialization (ISupportInitialize.EndInit). Triggers collection members must be of type EventTrigger". I've tried adding it to the trigger collection of the UserControl and StackPanel with no success. My trigger XAML looks like the following:

<DataTrigger Binding="{Binding ElementName=ToggleLaunch, Path=IsChecked}" Value="True">
    <Setter TargetName="Launch" Property="UIElement.Visibility" Value="Visible"/>
</DataTrigger>

For example, if you attempt to add a trigger to a TextBlock, it will generate this error:

Error: Triggers collection members must be of type EventTrigger

Why? A Trigger can only be placed inside a Style, ControlTemplate or DataTemplate, and we are trying to place it directly inside a TextBlock.

In this case, the fix is easy: just wrap the trigger in a style, then place this style inside the TextBlock, and the error will disappear.

Here is the error-generating XAML before the fix:

<TextBlock x:Name="Hello" Text="{Binding Hello, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
  <TextBlock.Triggers>
      <DataTrigger Binding="{Binding Hello}" Value="GoGreen">
          <Setter Property="Foreground" Value="Green" />
      </DataTrigger>
  </TextBlock.Triggers>
</TextBlock>

Here is the XAML after the fix:

<TextBlock x:Name="Hello" Text="{Binding Hello, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
    <TextBlock.Style>
        <Style TargetType="TextBlock">
            <Setter Property="Foreground" Value="Red" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Hello}" Value="GoGreen">
                    <Setter Property="Foreground" Value="Green" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </TextBlock.Style>
</TextBlock>

Here is a sample screenshot showing that if we enter GoGreen, the text goes green:

... and if we enter something else, the text defaults to red:

There is loads of free material on the web about WPF triggers, and all of them do a reasonably good job of explaining the concept, and this page was the one that made the penny drop for me.

If you already have a Style resource set on your UI control (as it happened to me), in order to avoid further errors, just turn that into the base of the local style having DataTrigger. As in: <TextBlock.Style> <Style TargetType="TextBlock" BasedOn="{StaticResource YourPreviousStyleResource}"> <Style.Triggers> <DataTrigger ... HTH – superjos Mar 11, 2015 at 17:29

DataTriggers are meant for Style, ControlTemplate and DataTemplate per the MSDN Docs.

Solution was to use an EventTrigger as the error message indicated. My solution was the following:

<EventTrigger RoutedEvent="ToggleButton.Checked">
    <BeginStoryboard>
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
                                           Storyboard.TargetName="LaunchButtons">
                <DiscreteObjectKeyFrame KeyTime="0:0:0"
                                        Value="{x:Static Visibility.Visible}" />
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </BeginStoryboard>
</EventTrigger>
<EventTrigger RoutedEvent="ToggleButton.Unchecked">
    <BeginStoryboard>
        <Storyboard>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Visibility"
                                           Storyboard.TargetName="LaunchButtons">
                <DiscreteObjectKeyFrame KeyTime="0:0:0"
                                        Value="{x:Static Visibility.Collapsed}" />
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>
    </BeginStoryboard>
 </EventTrigger>

You could also bind the Visibility in your stackpanel to the IsChecked property in the ToggleButton. You would need to use a custom ValueConverter. Here is one I found online:

/// <summary>  
/// WPF/Silverlight ValueConverter : Convert boolean to XAML Visibility
/// </summary>  
[ValueConversion(typeof(bool), typeof(Visibility))]
public class VisibilityConverter : IValueConverter
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
        value != null && (bool)value ? Visibility.Visible : Visibility.Collapsed;
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
        (Visibility)value == Visibility.Visible;

This may be hopelessly outdated, but the following works for me. It might help people running into the problem with: "Triggers collection members must be of type EventTrigger"

<Control>
  <Control.Template>
    <ControlTemplate >
      <!-- Design -->
      <StackPanel>
        <CheckBox Name="CollapseControl" Content="Show" IsChecked="False" />
        <Label Name="CollapseTarget" Content="MyContent" Visibility="Collapsed" />
      </StackPanel>
      <!-- Triggers -->
      <ControlTemplate.Triggers >
        <Trigger SourceName="CollapseControl" Property="IsChecked" Value="True" >
          <Setter TargetName="CollapseTarget" Property="Visibility" Value="Visible" />
        </Trigger>
      </ControlTemplate.Triggers>
    </ControlTemplate>
  </Control.Template>
</Control>

Encapsulating "what you want to control" inside a Control object allows you to use the Control.Template to use any trigger you want. This way you can use (data)triggers directly in your XAML where you want to without defining a static style or a completely new UserControl.

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.