Page 1 of 1

Storyboards in ControlTemplate

Posted: 23 Jan 2019, 12:31
by Aniak
Hi,
I've got a bunch of controls that I want to share the same behavior (some storyboards).
My approach was to create ControlTemplate with mentioned storyboards and bind it to controls through DynamicResource like that:
<Control x:Name="CTRL" VerticalAlignment="Top" Width="80" Template="{DynamicResource behavior}" Margin="193.662,16,0,0" HorizontalAlignment="Left" >

<ControlTemplate x:Key="some behavior" TargetType="{x:Type Control}">
	<ControlTemplate.Resources>
		<Storyboard x:Key="Action1">
		</Storyboard>
		<Storyboard x:Key="Action2">
		</Storyboard>
		<Storyboard x:Key="Action3">
		</Storyboard>
	</ControlTemplate.Resources>
</ControlTemplate>
but Begin() on storyboards doesn't work or I'm doing it the wrong way:
ControlTemplate *pTemplate = control->GetTemplate();
if (pTemplate)
{
	Storyboard *storyboard = NsStaticCast<Storyboard*>(pTemplate->GetResources()->FindName("Action1"));
	if (storyboard)
	{
		storyboard->Begin(control);
	}
}
What am I doing wrong? Or there is a better way to gave a controls common behavior.

Re: Storyboards in ControlTemplate

Posted: 23 Jan 2019, 18:40
by sfernandez
Hi and welcome to NoesisGUI forums,

Just to make sure I understand the scenario you want to solve, do you want to share the same behavior (animations) between different control types (Button, Label, TextBox, ListBox)? Or between different instances of the same control type?

Re: Storyboards in ControlTemplate

Posted: 24 Jan 2019, 08:03
by Aniak
Hi,
these are accualy different instances of the same control type (custom control for a game hud graphically showing states of some item). I'm new in Noesis, so maybe I should choose another approach to this problem.

Thank you for roseponding.

Re: Storyboards in ControlTemplate

Posted: 24 Jan 2019, 10:58
by sfernandez
In case you are talking about a particular type of control then the best way to implement this is by triggering your behavior animations with data properties.
A ControlTemplate is made of a VisualTree that defines the appearance of the control using other UI elements, and Triggers that define how these UI elements change depending on the state of the control given the values of its properties.
For example, a Button control template can look like this:
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
    <Border x:Name="Bg"
        Background="{TemplateBinding Background}"
        BorderBrush="{TemplateBinding BorderBrush}"
        BorderThickness="{TemplateBinding BorderThickness}"
        Padding="{TemplateBinding Padding}"
        CornerRadius="1">
        <ContentPresenter
            HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
            VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    </Border>
    <ControlTemplate.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="BorderBrush" Value="{StaticResource OverBdBrush}"/>
            <Setter Property="Background" Value="{StaticResource OverBgBrush}"/>
            <Setter Property="Foreground" Value="{StaticResource OverFgBrush}"/>
        </Trigger>
        <Trigger Property="IsPressed" Value="True">
            <Setter Property="BorderBrush" Value="{StaticResource PressBdBrush}"/>
            <Setter Property="Background" Value="{StaticResource PressBgBrush}"/>
            <Setter Property="Foreground" Value="{StaticResource OverFgBrush}"/>
        </Trigger>
        <Trigger Property="IsKeyboardFocused" Value="True">
            <Setter Property="BorderBrush" Value="{StaticResource FocusBdBrush}"/>
        </Trigger>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="BorderBrush" Value="{StaticResource DisabledBdBrush}"/>
            <Setter Property="Background" Value="{StaticResource DisabledBgBrush}"/>
            <Setter Property="Foreground" Value="{StaticResource DisabledFgBrush}"/>
        </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>
With the previous triggers and setters the Button will change the value of some properties for named elements inside the VisualTree to reflect the appearance of the new visual state.
You can do the same with your control and any dependency properties you want to expose.

There is also another option to define how a control should look depending on different states of the control, by using a VisualStateManager inside its ControlTemplate. You can read more about this topic here:
http://miteshsureja.blogspot.com/2011/0 ... re-in.html
http://miteshsureja.blogspot.com/2011/0 ... n-wpf.html

Default controls in Noesis define its own default states like MouseOver, Pressed, Disabled, Selected, Unselected, Focused, Unfocused, ...
With this approach you will be able to define custom VisualStates for your control that you can trigger from code:
void MyControl::ChangeState(State state)
{
  if (state == State::Alive)
    GoToState(NsSymbol("Alive"));
  else
    GoToState(NsSymbol("Dead"));
}
<VisualStateGroup x:Name="PlayerStates">
    <VisualStateGroup.Transitions>
        <VisualTransition GeneratedDuration="0:0:0.5"/>
    </VisualStateGroup.Transitions>
    <VisualState x:Name="Alive"/>
    <VisualState x:Name="Dead">
        <Storyboard>
            <ColorAnimationUsingKeyFrames
                    Storyboard.TargetProperty="(Shape.Fill).(SolidColorBrush.Color)"
                    Storyboard.TargetName="ellipse">
                <EasingColorKeyFrame KeyTime="0" Value="#FF80050E"/>
            </ColorAnimationUsingKeyFrames>
        </Storyboard>
    </VisualState>
</VisualStateGroup>
NOTE: Whenever is possible use StaticResource to refer to previously defined resources, that way you avoid creating an expression that is listening for resource changes in the UI tree.

Re: Storyboards in ControlTemplate

Posted: 24 Jan 2019, 14:33
by Aniak
Thank you, the approach with VisualStateManager worked great.