,

Modifying Page Elements Using Visual States

The visibility of the search panel and itinerary list is controlled using visual states. The visual state of either control is set via an associated string property in the viewmodel. Using a visual state, rather than a Boolean property for visibility, affords greater flexibility because you can create any number of visual state values, whereas a Boolean value allows you just two. See the following viewmodel excerpt:

public const string ShowRouteSearchState = "ShowRouteSearch";
public const string HideRouteSearchState = "HideRouteSearch";
string routeSearchVisualState = HideRouteSearchState;
public const string ShowItineraryState = "ShowItinerary";
public const string HideItineraryState = "HideItinerary";
...

string visualState = HideItineraryState;

public string VisualState
{
    get
    {
        return visualState;
    }
    private set
    {
        Assign(ref visualState, value);
    }
}

Commands such as the routeSearchToggleCommand and the itineraryToggleCommand are used to switch the visual state properties. The commands are initialized in the viewmodel constructor, as shown:

public MapViewModel(IRouteCalculator routeCalculator)
{
    ...
    routeSearchToggleCommand = new DelegateCommand(
        obj => ToggleRouteSearchVisibility());
    itineraryToggleCommand = new DelegateCommand(
        obj => ToggleItineraryVisibility());

    ...
}

The toggle commands, such as the RouteSearchToggleCommand, each set a corresponding visual state property to an alternative state, as shown:

void ToggleRouteSearchVisibility()
{
    if (routeSearchVisualState == ShowRouteSearchState)
    {
        VisualState = routeSearchVisualState = HideRouteSearchState;
    }
    else
    {
        VisualState = routeSearchVisualState = ShowRouteSearchState;
    }
}

The MapView page contains various visual states that coincide with the viewmodel’s VisualState properties (see Listing 18.6).

In the sample, the task of the Storyboard elements within the visual states is to either hide or reveal their associated target control or to update AppBar button and menu item text.

LISTING 18.6. MapView.xaml VisualStateGroups Excerpt


<Grid x:Name="LayoutRoot" Background="Transparent">
    ...
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="RouteStates">
            <VisualStateGroup.Transitions>
                <VisualTransition To="ShowRouteSearch" />
                <VisualTransition To="HideRouteSearch" />
            </VisualStateGroup.Transitions>
            <VisualState x:Name="ShowRouteSearch">
                <Storyboard>
                    <DoubleAnimation
                            Duration="0:0:0.3" To="20"
                            Storyboard.TargetProperty=
                  "(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
                            Storyboard.TargetName="RouteSearchView"
                            d:IsOptimized="True">
                        <DoubleAnimation.EasingFunction>
                            <CircleEase EasingMode="EaseIn" />
                        </DoubleAnimation.EasingFunction>
                    </DoubleAnimation>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="HideRouteSearch">
                <Storyboard>
                    <DoubleAnimation
                            Duration="0:0:0.3" To="-218"
                            Storyboard.TargetProperty=
                   "(UIElement.RenderTransform).(CompositeTransform.TranslateY)"
                            Storyboard.TargetName="RouteSearchView"
                            d:IsOptimized="True">
                        <DoubleAnimation.EasingFunction>
                            <CircleEase EasingMode="EaseOut" />
                        </DoubleAnimation.EasingFunction>
                    </DoubleAnimation>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
        ...
        <VisualStateGroup x:Name="ColorModeStates">
            <VisualStateGroup.Transitions>
                <VisualTransition To="LightMode" />
                <VisualTransition To="DarkMode" />
            </VisualStateGroup.Transitions>
            <VisualState x:Name="LightMode">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames
                            Storyboard.TargetProperty="Text"
                            Storyboard.TargetName="toggleColorModeMenuItem">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="dark mode"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
            <VisualState x:Name="DarkMode">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames
                            Storyboard.TargetProperty="Text"
                            Storyboard.TargetName="toggleColorModeMenuItem">
                        <DiscreteObjectKeyFrame KeyTime="0" Value="light mode"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
        ...
    </VisualStateManager.VisualStateGroups>

</Grid>


When the VisualState of the viewmodel changes to ShowRouteSearch, for example, the VisualStateManager is directed to that state and the RouteSearchView Border control is brought into view. This is all orchestrated using a custom VisualStateUtility class and an attached property (see Listing 18.7).

The VisualState attached property can be placed on a Control element and automatically transitions the visual state of the control using the VisualStateManager according to the value of the attached property.

LISTING 18.7. VisualStateUtility Class


public static class VisualStateUtility
{
    public static readonly DependencyProperty VisualStateProperty
        = DependencyProperty.RegisterAttached(
            "VisualState",
            typeof(string),
            typeof(VisualStateUtility),
            new PropertyMetadata(HandleVisualStateChanged));

    public static string GetVisualState(DependencyObject obj)
    {
        return (string)obj.GetValue(VisualStateProperty);
    }

    public static void SetVisualState(DependencyObject obj, string value)
    {
        obj.SetValue(VisualStateProperty, value);
    }

    static void HandleVisualStateChanged(
        object sender, DependencyPropertyChangedEventArgs args)
    {
        var control = sender as Control;
        if (control != null)
        {
            object newValue = args.NewValue;
            string stateName = newValue != null ? newValue.ToString() : null;

            if (stateName != null)
            {
                /* Call is invoked as to avoid missing
                    * the initial state before the control has loaded. */
                Deployment.Current.Dispatcher.BeginInvoke(
                    delegate
                        {
                            VisualStateManager.GoToState(control, stateName, true);
                        });
            }
        }
        else
        {
            throw new ArgumentException(
                "VisualState is only supported for Controls.");
        }
    }
}


The attached property is set on the phone:PhoneApplicationPage element of the MapView page, as shown:

<phone:PhoneApplicationPage
    ...
    u:VisualStateUtility.VisualState="{Binding VisualState}"
    ...>

When the viewmodel’s VisualState property changes, the HandleVisualStateChanged method of the VisualStateUtility class is called, which calls the built-in VisualStateManager.GoToState method.

The advantage of this approach is that it becomes unnecessary to subscribe to viewmodel property changed events from the page code-beside.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.17.62.34