Search code examples
wpfxaml

Trigger's Setter only fires once in WPF


The trigger should fire every single time the condition matches, but in my case it isn't.

I created a sidebar menu item using custom control. In it there's a property called IsActive, which returns a boolean. I bind this property to a Property in my ViewModel. Everytime the IsActive be true, I want it to change the foreground color to the color of HoverColor property which is Primary. If it isn't, stay at Black color.

But it doesn't happen, eventhough the IsActive is constantly changing its value and the trigger works as expected. But the Setter for the color only fires once. Even weird, when I MouseEnter and MouseLeave the item, the color changes back to Black, something that I don't specify at all in my trigger's conditions.

Here's the snippet of the code:

MainWindow.cs

        <controls:DrawerSidebarMenu 
            HorizontalAlignment="Left"
            ClosedWidth="50" 
            OpenedWidth="145"
            Background="{StaticResource VeryLightGrey}" 
            Foreground="{StaticResource Primary}"
            Padding="12">
            <StackPanel>
                <controls:DrawerSidebarMenuItem 
                    Kind="Stopwatch" 
                    Text="Timer"
                    Style="{StaticResource DrawerSidebarMenuItemDefault}"
                    Command="{Binding NavigateToTimerCommand}"
                    IsActive="{Binding CurrentViewModel, Converter={StaticResource TimerVMToBoolConv}}"
                   />
                <controls:DrawerSidebarMenuItem 
                    Kind="ListTask" 
                    Text="Tasks"  
                    Style="{StaticResource DrawerSidebarMenuItemDefault}"
                    Command="{Binding NavigateToTasksCommand}"
                    IsActive="{Binding CurrentViewModel, Converter={StaticResource TasksVMToBoolConv}}"
                    />
                <controls:DrawerSidebarMenuItem
                    Kind="BarChart" 
                    Text="Stats" 
                    Style="{StaticResource DrawerSidebarMenuItemDefault}"
                    Command="{Binding NavigateToStatsCommand}"
                    IsActive="{Binding CurrentViewModel, Converter={StaticResource StatsVMToBoolConv}}"
                    />
                <controls:DrawerSidebarMenuItem
                    Kind="Gear"
                    Text="Settings" 
                    Style="{StaticResource DrawerSidebarMenuItemDefault}"
                    Command="{Binding NavigateToSettingsCommand}"
                    IsActive="{Binding CurrentViewModel, Converter={StaticResource SettingsVMToBoolConv}}"
                    />
            </StackPanel>
            
        </controls:DrawerSidebarMenu>

In the ControlTemplate of the DrawerSidebarMenuItem, I defined the trigger as follows.

Theme/Generic.xaml

 <ControlTemplate.Triggers>
     <Trigger Property="IsActive" Value="True">
          <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=HoverColor}"/>
      </Trigger>
</ControlTemplate.Triggers>

Here's the screenshot it doesn't work: demo

Also, here's the proof that the IsActive property is constantly changing: IsActive works as expected demo

I've also included the project on a branch in my repo if you need further investigation: https://github.com/ihsansfd/extended-pomodoro/tree/stackoverflow

Any help would be appreciated!

I've tried using DataTrigger and Property Trigger, both give the same result. I expect it to fire every single time it matches the given condition.


Solution

  • As far as I understand, this is what you want to do: when the item is selected, you want the foreground of the item to change to the over color. You do not care about hovering and responing to hovering triggers.

    Thanks for providing your source code.

    It seems like you are doing the same thing, in two different location, using two different methods. Here are the things you are doing:

    1. You are responding to hover events in the code behind of your DrawerSidebarMenuItem. You are doing this in the OnApplyTemplate():
            public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
    
                if (HoverColor != null)
                {
                    var hoverColor = this.HoverColor;
                    var foreground = this.Foreground;
    
    // you set event listeners here
                    this.MouseEnter += (object sender, MouseEventArgs e) =>
                    {
                        this.Foreground = hoverColor;
                    };
    
                    this.MouseLeave += (object sender, MouseEventArgs e) =>
                    {
                        this.Foreground = foreground;
                    };
                }
    
            }
    
    1. You are trying to change the same parameters based on a trigger, that you convert using a custom converter. You do this in your Themes.Generic for your DrawerSidebarMenuItem component:
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsActive" Value="True">
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=HoverColor}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
    

    From this utilization, the following is happening:

    1. Mouse enters -> color changes to PRIMARY
    2. IsActive = True -> color changes to PRIMARY
    3. Mouse leaves -> color changes to BLACK

    All you need to do is remove the mouse enter event listeners in your OnApplyTemplate() and you should be good to go.

    If you want the color to change back when IsActive = false, add another trigger to your style:

                        <ControlTemplate.Triggers>
                            <Trigger Property="IsActive" Value="True">
                                <Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Self}, Path=HoverColor}"/>
                            </Trigger>
                            <Trigger Property="IsActive" Value="False">
                                <Setter Property="Foreground" Value="{StaticResource = whatever resource for your black color, Path=HoverColor}"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
    

    where you can input your reference to your black color resource.