Windows 10 UWP–Migrating a Windows 8.1 App (Part 8 of N)

Following on from this series of posts;

Windows 10 UWP–Migrating a Windows 8.1 App (Part 1 of N where N tends to infinity)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 2 of N)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 3 of N)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 4 of N)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 5 of N)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 6 of N)

Windows 10 UWP–Migrating a Windows 8.1 App (Part 7 of N)

I wanted to experiment a little more with providing a camera ‘wheel’ control on the right hand side of my UI – something a little like the one in the screenshot below – which would allow my user to manually control the focus of the camera on a device where the camera allows that focus.

image

In the previous post, I’d written a new, simple control that I called a RadialValueSelector which would largely do the job that I wanted but I now need to position this on the right hand edge of my UI and have it appear/disappear at the appropriate times.

I thought I’d write up my experience with that as it led me to some new things that I hadn’t really thought about with Visual State management before in the light of the new capability in Windows 10 UWP for Visual States to contain Setters rather than animations.

Let’s say that I want to display a simple half-circle on the right hand edge of my UI and I want that circle to take up some percentage of the available height such that it doesn’t fill the vertical space but that it resizes proportionally (i.e. not with a fixed margin) as the vertical space available to it expands.

I would usually do this with a Grid. For example – here in Blend I’ve set up a Grid where the central row is going to be 80% of the available height;

image

and so that’s fairly easy to do and I can drop my Circle in there and tell it to stretch itself vertically but then I hit a little bit of a problem;

image

How do I tell the Ellipse that I still want it to be a Circle? If I set its HorizontalAlignment to anything other than Stretch then it’s going to just disappear. However, if I give it a fixed Width, Height then it’s going to not scale itself nicely.

The way that I’d usually deal with this is to use a ViewBox – I often find this to be a useful technique when dealing with XAML content where I want to have some control over the co-ordinate system (e.g. maybe I want to draw a chessboard as a 640×640 grid) and yet I also want that content to scale when presented with more space.

If I give my Circle a fixed size and wrap a ViewBox around it;

image

then it behaves nicely and I can then align it on the right hand edge of the screen which is where I want it to slide in/out from;

image

but I’d really like it to be off to the right of the screen by its whole width in its usual state and so I can use a transform to move it off the screen. Given that I’m always drawing my Ellipse at a scaled 100×100, I can move it to the right by 100;

image

So far, so simple. My next step would be to define 2 visual states. One which is ‘off screen’ which is where the Ellipse currently is and another which is ‘on screen’ which would have the Ellipse half on the screen.

I can easily add a Visual State Group in Blend with those 2 Visual States within them;

image

and then in the OnScreen state, I can simply tell Blend to change the translation offset from 100 to 50;

image

and I’m good in that I can toggle between my OnScreen and OffScreen states in Blend and it shows me the right thing – i.e. the Ellipse moves in and out as I’d expect.

However, what Blend has just generated for me is as below;

 <VisualStateManager.VisualStateGroups>
      <VisualStateGroup
        x:Name="VisualStateGroup">
        <VisualState
          x:Name="OnScreen">
          <VisualState.Setters>
            <Setter
              Target="ellipse.(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
              Value="50" />
          </VisualState.Setters>
        </VisualState>
        <VisualState
          x:Name="OffScreen" />
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

and, as you’d expect, Blend is doing the ‘right thing’ here because it’s using the new capabilities here in Windows 10/UWP in order to define a visual state transition in terms of Setters which simply alter a value from 100 to 50.

In the old days, this wasn’t possible and these types of state transitions had to be defined in terms of animations and to achieve a simple setter effect you tended to have an animation with a duration of 0 seconds to act as a simple switch.

In my case, this was of interest to me because I don’t want a simple switch. I want my Ellipse to animate in from the edge of the screen in maybe 0.25s or something along those lines.

In this new world where Blend generates Setters rather than animations it wasn’t (or perhaps still isn’t) immediately obvious to me how I make that work and that drove me to write this post to walk through the various ways I tried out.

Trial 1 – Default Transition Duration

The first thing I tried was I tried setting the ‘default transition’ duration for my state transitions within my state groups. That is, I tried changing this;

image

Which has this effect in my XAML;

  <VisualStateManager.VisualStateGroups>
      <VisualStateGroup
        x:Name="VisualStateGroup">
        <VisualStateGroup.Transitions>
          <VisualTransition
            GeneratedDuration="0:0:2" />
        </VisualStateGroup.Transitions>
        <VisualState
          x:Name="OnScreen">
          <VisualState.Setters>
            <Setter
              Target="ellipse.(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
              Value="50" />
          </VisualState.Setters>
        </VisualState>
        <VisualState
          x:Name="OffScreen" />
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

and what that seems to do to my state changes is essentially delay them by 2 seconds (I exaggerated the time so that I could see what was happening).

That’s not really the effect that I wanted and so I figured I’d add transition animations myself.

Trial 2 – Adding State Transitions

What if I add animations for the transition in/out of my OnScreen state? That is…I can add Storyboards that tell the system how to make the transition as per the screenshot below;

image

And so I’m now saying that whenever we transition into the ‘OnScreen’ state we need to animate the X translation value from 100 to 50 over a 2s period and the reverse for the transition away from the OnScreen state.

This leaves me with XAML;

    <VisualStateManager.VisualStateGroups>
      <VisualStateGroup
        x:Name="VisualStateGroup">
        <VisualStateGroup.Transitions>
          <VisualTransition
            GeneratedDuration="0" />
          <VisualTransition
            From="OnScreen"
            GeneratedDuration="0">
            <Storyboard>
              <DoubleAnimationUsingKeyFrames
                Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
                Storyboard.TargetName="ellipse">
                <EasingDoubleKeyFrame
                  KeyTime="0"
                  Value="50" />
                <EasingDoubleKeyFrame
                  KeyTime="0:0:2"
                  Value="100" />
              </DoubleAnimationUsingKeyFrames>
            </Storyboard>
          </VisualTransition>
          <VisualTransition
            GeneratedDuration="0"
            To="OnScreen">
            <Storyboard>
              <DoubleAnimation
                Duration="0:0:2"
                To="50"
                Storyboard.TargetProperty="(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
                Storyboard.TargetName="ellipse"
                d:IsOptimized="True" />
            </Storyboard>
          </VisualTransition>
        </VisualStateGroup.Transitions>
        <VisualState
          x:Name="OnScreen">
          <VisualState.Setters>
            <Setter
              Target="ellipse.(UIElement.RenderTransform).(CompositeTransform.TranslateX)"
              Value="50" />
          </VisualState.Setters>
        </VisualState>
        <VisualState
          x:Name="OffScreen" />
      </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>

and it seems to work fine both in the designer’s ‘preview transition’ mode and at runtime.

I must admit that this just seeming to work threw me a little as I’m still a little unsure of the relationship between Setters here and the transition animations and yet it looks like the Setters are applied after the transition animations have been run which is what I’d perhaps hope for but I hadn’t necessarily expected it to be the case and it’s possible that I’m being fooled by the simplicity of the example here.

Naturally, if you know more on how these things are meant to combine, feel free to add comments below and I’ll incorporate into the post. For now, I think I’ve got the visual states that I wanted sorted out and I’ll continue the port of my app to Windows 10.