1
votes

In WPF, I have a ListBox with the list made up of UserControls. The controls are meant to navigate to different screens in the application. Each UserControl (called NavigationButton) has an icon and text. The icons are mostly combinations of multiple Path objects, so each icon is it's own UserControl, and they are being displayed using a ContentPresenter. I want to be able to animate the color of the icon depending on different states of the screen, but have tried a lot of options and have been unable to do this.

Here is a stripped down version of NavigationButton:

<DockPanel Margin="12,0,12,0">

        <!-- Icon -->
        <ContentPresenter x:Name="Content_Icon" Content="{Binding}" Width="20"/>

        <!-- Text -->
        <Grid Margin="9,0,0,0">
            <TextBlock x:Name="TextBlock_Text" Text="{Binding ScreenName, Converter={StaticResource StringToStringUpperConverter}}" VerticalAlignment="Center" 
                       FontSize="15" Foreground="#FFF2F2F2" />
        </Grid>

Basically, I need to animate a property on the ContentPresenter, but don't know how to access it.

Here is the ListBox hosting the NavigationButtons:

        <ListBox DockPanel.Dock="Top" ItemsSource="{Binding ScreenViewModels}" 
             SelectedItem="{Binding SelectedScreenViewModel}">

        <ListBox.ItemTemplate>
            <DataTemplate>
                <my:NavigationButton/>
            </DataTemplate>
        </ListBox.ItemTemplate>

I have created a base UserControl (called IconBaseControl) that all of these icon UserConrols can inherit. The base control has a Brush DependencyProperty, called IconFill. The parts of the paths on the icon that can change are bound to this property:

<Path Data="<data>" Fill="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type my:IconBaseControl}}, Path=IconFill}"

I know the binding is working correctly because the colors change when I change the default color on the UserControl. Ideally I want to use a VisualStateManager, because there will be many different states. So, I have a VisualStateManager on NavigationButton, the UserControl containing the ContentPresenter that hosts the icon (all UserControls that inherit IconBaseControl), called Content_Icon. I tried something like this in one of the states:

<VisualState x:Name="Deselected">
            <Storyboard>

                <ColorAnimation Storyboard.TargetName="TextBlock_Text" Storyboard.TargetProperty="Foreground.Color"
                        To="#FF5e5e5e" Duration="0"/>

                <ColorAnimation Storyboard.TargetName="Content_Icon" Storyboard.TargetProperty="IconFill"
                        To="#FF5e5e5e" Duration="0"/>

            </Storyboard>
</VisualState>

But I get the following error:

InvalidOperationException: Cannot resolve all property references in the property path 'IconFill'. Verify that applicable objects support the properties.

I also tried binding the property of the storyboard with something like this:

Storyboard.TargetProperty="(IconBaseControl.IconFill)

But get this error:

IconBaseControl is not supported in a Windows Presentation Foundation (WPF) project.

I have also tried messing around in code behind but cannot figure out how to convert the ContentPresenter to an IconBaseControl. I figured the ContentTemplate property would be the way to go but it's Nothing.

Any suggestions on how to animate this property? Open to pretty much anything :) I'm coding in VB.Net but any C# suggestions are fine too.

Thanks in advance.

EDIT: Included code for NavigationButton

1

1 Answers

1
votes

I find that creating sub-classes of WPF controls can get messy and isn't necessary unless it is a very advanced problem. In my opinion creating the IconBaseControl as a child of UserControl is overkill in your scenario.

Here's my suggestion assuming you are using MVVM: create the IconBaseControl as a normal UserControl. Just create a IconControl.xaml with IconControl.xaml.cs code behind file just like you would any other view.

Here is an example of what you would have inside IconControl:

<UserControl>
    <Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.Style>
        <Style>
            <Style.Triggers>
                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                    <DataTrigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="#FF5e5e5e" Duration="0:0:0" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.EnterActions>
                    <DataTrigger.ExitActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <ColorAnimation Storyboard.TargetProperty="(Background).(SolidColorBrush.Color)" To="White" Duration="0:0:0" />
                            </Storyboard>
                        </BeginStoryboard>
                    </DataTrigger.ExitActions>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Grid.Style>

    <Image Source="Icon.jpeg" />

    <TextBlock Text="{Binding PageName}" Grid.Column="1" />

</Grid>
</UserControl>`

Notice that the background of the surrounding grid will change based on a binding to a value called IsSelected on the DataContext. So at this point you need to create a ViewModel called IconControlViewModel.cs that has the IsSelected boolean exposed as a dependency property.

Finally the view that contains these navigation buttons:

<UserControl>

    <ItemsControl ItemsSource="{Binding ListOf_IconControlViewModels}">
        <ItemsControl.ItemTemplate>
            <DataTemplate DataType="{x:Type IconControlViewModel}">
                <local:IconView />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

</UserControl>

Notice the DataTemplate that tells the ItemsControl what to render when it sees a IconControlViewModel in the ItemsSource list. This is how I would design it using the MVVM pattern. I hope this helps and let me know if you need clarification on my answer, or it's way off.

Cheers, Eric