Is it possible for individual listviewitems selected, pointerover and pressed states to be controlled by the data bound item

浪子不回头ぞ 提交于 2019-12-24 01:06:06

问题


I'm currently trying to style a ListView so each of the items inside it can control their own background color, PointerOver, Pressed and Selected states.

I have figured out that I can set the Background of the ListViewItem to Transparent and let the DataTemplate for my item control the Background. But what I'm struggling with is the VisualStates of the ListViewItem. They just won't seem to style correctly.

What I've tried so far is so far is putting my Binding code for the colors inside the Style for the ListViewItem like so:

NOTE: Code has been cut down for brevity, ListViewItem style taken from the Generic.xaml

<Style TargetType="ListViewItem">
    <Setters.../>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListViewItem">
                <Grid x:Name="ContentBorder">
                    <!-- ContentPresenter etc in here -->
                    <VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Pressed">
                                <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
                                    <DiscreteObjectKeyFrame KeyTime="0" Value="{Binding AccentColour}" />
                                </ObjectAnimationUsingKeyFrames>
                            </VisualState>
                            <!-- More Visual States-->
                        </VisualStateGroup>
                    </VisualStateManager.VisualStateGroups>
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

However this doesn't work and I assume it has something to do with the ListViewItem not knowing the DataContext for the Binding.

My item is just a basic class to hold info like the text associated with the item and the colors the for Background and the States.

public class BasicItem {
    // Constructor etc

    public SolidColorBrush AccentColour { get; set; }
    // More Colours
}

Another approach I had considered was creating AttachedProperties for the Colors on the ListViewItem. Only issue is I'm not sure how I would bind to them.

Anyone have any idea if it's possible for the Item to control its States in this way, any help appreciated.


回答1:


Another approach I had considered was creating AttachedProperties for the Colors on the ListViewItem. Only issue is I'm not sure how I would bind to them.

You are right about using AttachedProperties and this is how it can be done.

Step 1. Define your attached properties

public static Brush GetPointerOverBackground(DependencyObject obj)
{
    return (Brush)obj.GetValue(PointerOverBackgroundProperty);
}
public static void SetPointerOverBackground(DependencyObject obj, Brush value)
{
    obj.SetValue(PointerOverBackgroundProperty, value);
}
public static readonly DependencyProperty PointerOverBackgroundProperty =
    DependencyProperty.RegisterAttached("PointerOverBackground", typeof(Brush), typeof(YourStaticClass), new PropertyMetadata(null));

Step 2. Define default values for your attached properties

<Setter Property="local:YourStaticClass.PointerOverBackground" Value="LightBlue" />

Step 3. Use attached properties in your Visual States

<VisualState x:Name="PointerOver">
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ContentPresenter" Storyboard.TargetProperty="Background">
        <DiscreteObjectKeyFrame KeyTime="0" Value="{TemplateBinding local:YourStaticClass.PointerOverBackground}" />
    </ObjectAnimationUsingKeyFrames>
</VisualState>

Step 4. Create an Interface that defines all the brushes

The reason of doing this is so we can later cast the object (i.e. BasicItem) bound to each template to this Interface, without the need of knowing its concrete type. Of course your BasicItem should implement this Interface.

public interface ISupportListViewItemBrushes
{
    SolidColorBrush PointerOverBrush { get; set; }

Step 5. Create a Behavior responsible for assigning Brushes to ListViewItems

This is where everything comes together. First we will need to install the XAML Behavior Nuget Package. After that, create a Behavior called SupportListViewItemBrushesBehavior and make it only attachable to a ListView.

public class SupportListViewItemBrushesBehavior : Behavior<ListView>

ListView has an event called ContainerContentChanging where we have access to each ListViewItem and its underlying Item (i.e. BasicItem/ISupportListViewItemBrushes) object. Finally, we just use the SetAttachedPropertyName method to assign the corresponding Brush to the ListViewItem like below

private void OnContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args)
{
    if (!args.InRecycleQueue && 
        args.ItemContainer is ListViewItem container && 
        args.Item is ISupportListViewItemBrushes brushes)
    {
        Helper.SetPointerOverBackground(container, brushes.PointerOverBrush);
    }
}

That's all!


Note we had to create the Behavior here because UWP doesn't support FindAncestor binding yet. Alternatively, rather than using attached properties and a Behavior, you can extend ListView/ListViewItem to achieve the same result.

I have included a small sample here for your reference. And here's the end result. Hope this helps!




回答2:


Implement a StyleSelector that chooses the ListViewItem style you want for a given item, and set it as the ItemContainerStyleSelector on your ListView.



来源:https://stackoverflow.com/questions/45599788/is-it-possible-for-individual-listviewitems-selected-pointerover-and-pressed-st

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!