Adam Dernis
Adam Dernis

Reputation: 530

Making a custom control based on the ToggleButton that switches content when checked (VisualState problems)

This is my ToggleContentButton that adds a CheckedContent to the ToggleButton.


    /// <summary>
    /// A <see cref="ToggleButton"/> that can change content when toggled
    /// </summary>
    public sealed partial class ToggleContentButton : ToggleButton
    {
        /// <summary>
        /// A <see cref="DependencyProperty"/> for the <see cref="CheckedContent"/> property.
        /// </summary>
        public static readonly DependencyProperty CheckedContentProperty = DependencyProperty.Register(
            nameof(CheckedContent),
            typeof(object),
            typeof(ToggleContentButton),
            new PropertyMetadata(null));

        /// <summary>
        /// Initializes a new instance of the <see cref="ToggleContentButton"/> class.
        /// </summary>
        public ToggleContentButton()
        {
            this.DefaultStyleKey = typeof(ToggleContentButton);
        }

        /// <summary>
        /// The <see cref="UIElement"/> the <see cref="ToggleContentButton"/> shows when toggled.
        /// </summary>
        public object CheckedContent
        {
            get { return (object)GetValue(CheckedContentProperty); }
            set { SetValue(CheckedContentProperty, value); }
        }
    }

This is how I define the default style for my new Control, but the VisualStates are completely broken.

    <Style x:Key="DefaultToggleContentButtonStyle" TargetType="local:ToggleContentButton">
        <Style.Setters>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="local:ToggleContentButton">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal"/>
                                    <VisualState x:Name="MouseOver">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#F2FFFFFF"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#CCFFFFFF"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#7FFFFFFF"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Pressed">
                                        <Storyboard>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="#FF6DBDD1"/>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" To="#D8FFFFFF"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#C6FFFFFF"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#8CFFFFFF"/>
                                            <ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#3FFFFFFF"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Disabled">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To=".55"/>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="CheckStates">
                                    <VisualState x:Name="Checked">
                                        <VisualState.Setters>
                                            <Setter Target="checkedContentPresenter.Visibility" Value="Visible"/>
                                            <Setter Target="contentPresenter.Visibility" Value="Collapased"/>
                                        </VisualState.Setters>
                                        <Storyboard>
                                            <PointAnimation Duration="0" To="0.7,1" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(LinearGradientBrush.StartPoint)"/>
                                            <PointAnimation Duration="0" To="0.7,0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(LinearGradientBrush.EndPoint)"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unchecked">
                                        <VisualState.Setters>
                                            <Setter Target="checkedContentPresenter.Visibility" Value="Collapased"/>
                                            <Setter Target="contentPresenter.Visibility" Value="Visible"/>
                                        </VisualState.Setters>
                                    </VisualState>
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Focused">
                                        <Storyboard>
                                            <DoubleAnimation Duration="0" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1"/>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="Unfocused" />
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>
                            <Border x:Name="Background" CornerRadius="3" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
                                <Grid Background="{TemplateBinding Background}"  Margin="1">
                                    <Border Opacity="0" x:Name="BackgroundAnimation" Background="#FF448DCA" />
                                    <Rectangle x:Name="BackgroundGradient" >
                                        <Rectangle.Fill>
                                            <LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1">
                                                <GradientStop Color="#FFFFFFFF" Offset="0" />
                                                <GradientStop Color="#F9FFFFFF" Offset="0.375" />
                                                <GradientStop Color="#E5FFFFFF" Offset="0.625" />
                                                <GradientStop Color="#C6FFFFFF" Offset="1" />
                                            </LinearGradientBrush>
                                        </Rectangle.Fill>
                                    </Rectangle>
                                </Grid>
                            </Border>
                            <ContentPresenter
                              x:Name="contentPresenter"
                              Content="{TemplateBinding Content}"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}"/>
                            <ContentPresenter
                              x:Name="checkedContentPresenter"
                              Content="{TemplateBinding CheckedContent}"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              Margin="{TemplateBinding Padding}"/>
                            <Rectangle x:Name="DisabledVisualElement" RadiusX="3" RadiusY="3" Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />
                            <Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Margin="1" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style.Setters>
    </Style>

    <Style BasedOn="{StaticResource DefaultToggleContentButtonStyle}" TargetType="local:ToggleContentButton"/>

Is there a proper and better way to do this? How can I implement the VisualStates?

Upvotes: 0

Views: 36

Answers (1)

marcelwgn
marcelwgn

Reputation: 969

The ToggleButton does not use separate VisualState groups for the Checked state, but rather has them in the common states group. So you would need to do something like this to hide/show them appropriately:

<VisualStateManager.VisualStateGroups>
    <VisualStateGroup x:Name="CommonStates">
        <VisualState x:Name="Normal">
        </VisualState>
        <VisualState x:Name="PointerOver">
        </VisualState>
        <VisualState x:Name="Pressed">
        </VisualState>
        <VisualState x:Name="Disabled">
        </VisualState>
        <VisualState x:Name="Checked">
            <VisualState.Setters>
                <Setter Target="checkedContentPresenter.Visibility" Value="Visible"/>
                <Setter Target="contentPresenter.Visibility" Value="Collapsed"/>
            </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="CheckedPointerOver">
            <VisualState.Setters>
                <Setter Target="checkedContentPresenter.Visibility" Value="Visible"/>
                <Setter Target="contentPresenter.Visibility" Value="Collapsed"/>
            </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="CheckedPressed">
            <VisualState.Setters>
                <Setter Target="checkedContentPresenter.Visibility" Value="Visible"/>
                <Setter Target="contentPresenter.Visibility" Value="Collapsed"/>
            </VisualState.Setters>
        </VisualState>
        <VisualState x:Name="CheckedDisabled">
            <VisualState.Setters>
                <Setter Target="checkedContentPresenter.Visibility" Value="Visible"/>
                <Setter Target="contentPresenter.Visibility" Value="Collapsed"/>
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

Upvotes: 1

Related Questions