XAML ListView - Change Image Source for selected i

2020-04-21 18:21发布

问题:

I'm using a ListView with a Custom Template, something like this:

<ListView.ItemTemplate>
    <DataTemplate>
        <Grid HorizontalAlignment="Center" Width="220" Height="220">
            <Image x:Name="image" Stretch="UniformToFill" 
                    Source="{Binding Brand.Image, 
                             ConverterParameter=transparent, 
                             Converter={StaticResource LogoToUriConverter}}"/>
            <StackPanel VerticalAlignment="Bottom">
                <TextBlock Text="{Binding Name}" 
                            Foreground="{StaticResource ApplicationColor}" 
                            Style="{StaticResource TitleTextStyle}" 
                            Height="30" Margin="15,0,15,0"/>
                <TextBlock Text="{Binding Name}" 
                            Foreground="{StaticResource ApplicationColor}" 
                            Style="{StaticResource CaptionTextStyle}" 
                            TextWrapping="NoWrap" Margin="15,0,15,10"/>
            </StackPanel>
        </Grid>
    </DataTemplate>
</ListView.ItemTemplate>

Now when an Item is selected I would like to have the image source for selected item changed to a new one.

Brand.Image is not a DependencyProperty because it comes from an external DataObject.

So, I think that in WPF I could use a Trigger to change it manually.

But since in winRT it does not work anymore, I've looked into VSM, but I'm not figuring out how can I accomplish that.

Can someone provide me a real example how could it be done?

Thank you

回答1:

I was able to solve this, in a tricky way, but I got it to work:

  1. Using an ExtendedVisualStateManager, (it was available for .NET through ExpressionBlend dlls, but not for WinRT, so I got it from here: http://nroute.codeplex.com/SourceControl/changeset/69480#nRoute5/nRoute.Framework.Metro/Components/ExtendedVisualStateManager.cs)

  2. Having that I just catch an OnSelected Event and use the new VisualStateManager to do that:

    ExtendedVisualStateManager.GoToElementState(sender as Grid, "Selected2", true);

Here's the full XAML for the ItemTemplate:

<DataTemplate>
<Grid x:Name="ItemGrid" HorizontalAlignment="Center" Width="220" Height="220" PointerPressed="GridItemTapped">
    <Image x:Name="image" Stretch="UniformToFill" Source="{Binding Brand.Name, ConverterParameter=white, Converter={StaticResource LogoToUriConverter}}"/>
    <Image x:Name="image_colored" Stretch="UniformToFill" Visibility="Collapsed" Source="{Binding Brand.Name, ConverterParameter=colored, Converter={StaticResource LogoToUriConverter}}"/>
    <StackPanel VerticalAlignment="Bottom">
        <TextBlock Text="{Binding Name}" Foreground="White" Style="{StaticResource TitleTextStyle}" Height="30" Margin="15,0,15,0"/>
        <TextBlock Text="{Binding Name}" Foreground="White" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
    </StackPanel>
    <VisualStateManager.CustomVisualStateManager>
        <vsm:ExtendedVisualStateManager/>
    </VisualStateManager.CustomVisualStateManager>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="SelectionStates">
            <VisualState x:Name="Selected2">
                <Storyboard>
                    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image" Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="00:00:00.0000000">
                            <DiscreteObjectKeyFrame.Value>
                                Collapsed
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                    <ObjectAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="image_colored" Storyboard.TargetProperty="Visibility">
                        <DiscreteObjectKeyFrame KeyTime="00:00:00.0000000">
                            <DiscreteObjectKeyFrame.Value>
                                Visible
                            </DiscreteObjectKeyFrame.Value>
                        </DiscreteObjectKeyFrame>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
</Grid>

Hope this can help anybody with the same issue.

If you have a better and easier way to achieve the same result in WinRT, please present your solution.

Thank you



回答2:

You can create a style for your ListViewItem, with a controltemplate for the triggers and a datatemplate for your data binding, like this:

   <Style x:Key="FocusedContainer" TargetType="{x:Type ListViewItem}">
        <EventSetter Event="GotKeyboardFocus" Handler="OnListBoxItemContainerFocused" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type ListViewItem}">
                    <Border x:Name="backgroundBorder">
                        <ContentPresenter Content="{TemplateBinding Content}">
                            <ContentPresenter.ContentTemplate>
                                <DataTemplate>
                                     <Grid HorizontalAlignment="Center" Width="220" Height="220">
                                <Image x:Name="image" Stretch="UniformToFill" Source="{Binding Brand.Image, ConverterParameter=transparent, Converter={StaticResource LogoToUriConverter}}"/>
                                <StackPanel VerticalAlignment="Bottom">
                                    <TextBlock Text="{Binding Name}" Foreground="{StaticResource ApplicationColor}" Style="{StaticResource TitleTextStyle}" Height="30" Margin="15,0,15,0"/>
                                    <TextBlock Text="{Binding Name}" Foreground="{StaticResource ApplicationColor}" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
                                </StackPanel>
                            </Grid>
                                </DataTemplate>
                            </ContentPresenter.ContentTemplate>
                        </ContentPresenter>
                    </Border>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter TargetName="image" Property="Source" Value="{**Insert your alternate binding here**}"
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Then configure your ListView like this:

<ListView ItemContainerStyle="{StaticResource FocusedContainer}"/>

You'll see that the style has an EventSetter: its purpose is to get the correct item selected even if you click inside some control (not directly on the background). You need to create the handler in code behind, just a couple of lines:

    private void OnListBoxItemContainerFocused(object sender, System.Windows.RoutedEventArgs e)
    { (sender as ListViewItem).IsSelected = true; }

Hope this is helpful, regards!