Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ToggleButton Control VisualStateManager: Handling Multiple Hover States

In reference to a previous question I had (Silverlight MVVM Confusion: Updating Image Based on State) I started with a new approach. I left the existing question because I didn't want to say for sure my new approach was the correct answer (I still welcome comments on my original problem).

If you read my previous question, please feel free to skip this paragraph: I'm trying to build a control that provides functionality similar to an audio play button. When the app is in its "Play" mode, the app should display the "Pause.png" image. When it's paused, it should display the "Play.png" image. There are also two additional images to account for either state for when a user hovers over the control (e.g., "Play_Hover.png" and "Pause_Hover.png"). The state is determined by the an IsPlaying property in my view model.

I decide to use a ToggleButton to determine which image to display based on the current status. Remember, when IsPlaying is false, the play button is displayed and when it's true, the pause button is displayed. As such, I came up with the following XAML. It works except for the hovers:

<UserControl x:Class="Foo.BarMyControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    mc:Ignorable="d"
    d:DesignHeight="100" d:DesignWidth="200">
    <UserControl.Resources>
        <Style x:Key="MyButtonStyle" TargetType="ToggleButton">
            <Setter Property="IsEnabled" Value="true"/>
            <Setter Property="IsTabStop" Value="true"/>
            <Setter Property="Background" Value="#FFA9A9A9"/>
            <Setter Property="Foreground" Value="#FF000000"/>
            <Setter Property="MinWidth" Value="5"/>
            <Setter Property="MinHeight" Value="5"/>
            <Setter Property="Margin" Value="0"/>
            <Setter Property="HorizontalAlignment" Value="Left" />
            <Setter Property="HorizontalContentAlignment" Value="Center"/>
            <Setter Property="VerticalAlignment" Value="Top" />
            <Setter Property="VerticalContentAlignment" Value="Center"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ToggleButton">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CheckStates">
                                    <VisualState x:Name="Checked">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Pause">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Play">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unchecked">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Play">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Visible</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Pause">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <Visibility>Collapsed</Visibility>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Indeterminate" />
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Image x:Name="Play" Source="/Foo.Bar;component/Resources/Icons/Bar/Play.png" />
                            <Image x:Name="Pause" Source="/Foo.Bar;component/Resources/Icons/Bar/Pause.png" Visibility="Collapsed" />
                            <Image x:Name="PlayHover" Source="/Foo.Bar;component/Resources/Icons/Bar/Play_Hover.png" Visibility="Collapsed" />
                            <Image x:Name="PauseHover" Source="/Foo.Bar;component/Resources/Icons/Bar/Pause_Hover.png" Visibility="Collapsed" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">
        <ToggleButton Style="{StaticResource MyButtonStyle}" IsChecked="{Binding LiveEnabled}" Command="{Binding ChangeStatus}" Height="30" Width="30" />
    </Grid>
</UserControl>

How do you have different hover images for both states? If I add a Hover state to the CommonStates group, I'll be able to account for hover of only one of the states (checked or unchecked).

like image 380
senfo Avatar asked Feb 25 '23 01:02

senfo


2 Answers

With the togglebutton it is not possible to have a different hover / Mouse over state as this is common to the button. Common states are Normal (what you initally see), Mouseover,Pressed and Disabled

The other states related to checked, unchecked or intermediate. Here as you have done you can set different images etc for the various state. The mouseover though will always roll back to the common state.

If you have to have this funcionality you could create your own custom control for this and handle the mouseover animation based upon the active state. This would require more code on the back-end as you will need to redefine the button class for this object and insert testing for the various states to allow a set animation to play for each state. It could be done I just don't know if it would be worth that much effort.

like image 88
rlcrews Avatar answered Mar 29 '23 23:03

rlcrews


you also can achieve this without custom control, like this:

        <ToggleButton  x:Name="PlayButton"
                       IsChecked="{Binding Path=IsPlayChecked, Mode=TwoWay}"
                         Width="30" Height="30"
                        Style="{StaticResource ToggleButtonStyle}">
        <ToggleButton.Content>
            <Grid Margin="3">
                <Grid Visibility="{Binding ElementName=PlayButton, Path=IsChecked, Converter={StaticResource visConverter}, ConverterParameter=Invert}" Margin="0">
                    <Image Source="../Resources/play_normal.png"></Image>
                    <Image Source="../Resources/play_hover.png" Visibility="{Binding ElementName=PlayButton, Path=IsMouseOver, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/play_hit.png" Visibility="{Binding ElementName=PlayButton, Path=IsPressed, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/play_disabled.png"  Visibility="{Binding ElementName=PlayButton, Path=IsEnabled, Converter={StaticResource visConverter}, ConverterParameter=Invert}"></Image>
                </Grid>
                <Grid Visibility="{Binding ElementName=PlayButton, Path=IsChecked, Converter={StaticResource visConverter}}">
                    <Image Source="../Resources/pause_normal.png"></Image>
                    <Image Source="../Resources/pause_hover.png" Visibility="{Binding ElementName=PlayButton, Path=IsMouseOver, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/pause_hit.png" Visibility="{Binding ElementName=PlayButton, Path=IsPressed, Converter={StaticResource visConverter}}"></Image>
                    <Image Source="../Resources/pause_disabled.png"  Visibility="{Binding ElementName=PlayButton, Path=IsEnabled, Converter={StaticResource visConverter}, ConverterParameter=Invert}"></Image>
                </Grid>
            </Grid>
        </ToggleButton.Content>
    </ToggleButton>

Where:

<Converters:BooleanToVisibilityConverter x:Key="visConverter"/>

And defined BooleanToVisibilityConverter like this:

public class BooleanToVisibilityConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var flag = (bool)value;
        if (parameter != null)
            flag = !flag;
        return (flag ? Visibility.Visible : Visibility.Collapsed);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return ((value is Visibility) && (((Visibility)value) == Visibility.Visible));
    }
}

Hope it'll help!

like image 24
Ivan Leonenko Avatar answered Mar 30 '23 01:03

Ivan Leonenko