Search code examples
c#silverlightvisualstatemanagervisualstates

Setting multiple visual states in one go?


I have four visual states defined each affecting different child controls within the same silver-light control. Is it possible for me to create other visual states which invoke a combination of these others?

So if I have Visual_Group_1, Visual_Group_2, Visual_Group_3, Visual_Group_4

  1. Is it possible to make, say a Visual_Comb_1 group which uses the states in Visual_Group_1 and Visual_Group_3?
  2. Then make another one called Visual_Comb_2 which uses Visual_Group_4 and Visual_Group_3?

I'm happy to implement a solution in xaml or codebehind or a combination of both. The alternative I'm looking at currently involves tonnes of code copy+paste and I'm not too keen to do that.

Some more detail per request:

This is what I roughly have right now:

<VisualState x:Name="State1">
    <ColorAnimation Storyboard.TargetName="Path1"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="Blue" Duration="0:0:0.5" />
    // fade out the rest of the paths...
    <ColorAnimation Storyboard.TargetName="Path2"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path3"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path4"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
</VisualState>

<VisualState x:Name="State2">
    <ColorAnimation Storyboard.TargetName="Path3"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="Red" Duration="0:0:0.5" />
    // fade out the rest of the paths...
    <ColorAnimation Storyboard.TargetName="Path2"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path1"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path4"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
</VisualState>

<VisualState x:Name="State3">
    <ColorAnimation Storyboard.TargetName="Path4"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="Pink" Duration="0:0:0.5" />
    // fade out the rest of the paths...
    <ColorAnimation Storyboard.TargetName="Path2"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path1"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
    <ColorAnimation Storyboard.TargetName="Path3"
                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                    To="#00000000" Duration="0:0:0.5" />
</VisualState>

My objective is to have a control which when you click on cycles from state1 to state3, each state fades in a different path while fading out the other paths. My problem is that there is a tonne of copy+paste in the 'fade out the rest of the paths' section, so if I wanted to add a Path5 it would mean adding it to every single visual state already defined, or if I wanted to change the fadeoff colour or animation I would have to do it to every visual state.


Solution

  • Thanks for providing the XAML. This is how I would tackle the problem.

    First off, create the VisualStates individually for each Path. (I would recommend using a Style instead to save you re-coding a very similar VisualState into each Path, but I'm not familiar enough with them to know if you can apply different colours to each.)

    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="Path1States">
            <VisualState x:Name="Activate">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="Path1"
                                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                                    To="Blue"
                                    Duration="0:0:0.5" />
                </Storyboard>
            </VisualState>
            <VisualState x:Name="Deactivate">
                <Storyboard>
                    <ColorAnimation Storyboard.TargetName="Path1"
                                    Storyboard.TargetProperty="(Path.Fill).(SolidColorBrush.Color)"
                                    To="#00000000"
                                    Duration="0:0:0.5" />
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
        <VisualStateGroup x:Name="Path2States">
            <!-- ... etc ... -->
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    

    Now, create a List in the code-behind that contains each of the related objects, and then right your own GoToState function such that it turns on the in state for one object, and calls the off state for the rest.

    List<Path> pathList;
    
    public Page() // constructor
    {
        InitializeComponent();
        pathList = new List<Path>();
        pathList.Add(Path1);
        // and so forth
    }
    
    // Call this function when you want to change the state
    private void ActivatePath(Path p)
    {
        foreach (Path listItem in pathList)
        {
            // If the item from the list is the one you want to activate...
            if (listItem == p)
                VisualStateManager.GoToState(listItem, "Activate", true);
            // otherwise...
            else
                VisualStateManager.GoToState(listItem, "Deactivate", true);
        }
    }
    

    If I were better at XAML and styling I might have a cleaner way of creating the VisualStates. However, my forte is more on the logic and coding side. That being said, it's much cleaner that writing out the same VisualState four or five times! :)
    Hope this helps!