Link selection between two ListViews

3

Would it be possible to bind the unique selection between two ListView objects via XAML?

For example, I have two ListView , TopListView and BottomListView , both configured for simple selection, ie only one item can be selected. However, since they are ListView independent, you can have an item selected in TopListView while there is one selected in BottomListView .

And I want to make them work as if they were one, that is, if an item is selected in TopListView , the item that is selected in BottomListView "deselected" and see versa.

I know how to do this via code-behind, manipulating the event SelectionChanged of each ListView . But I'm wondering if it's possible to do this without having to appeal to the code-behind.

    
asked by anonymous 08.01.2017 / 18:32

3 answers

2

Ramaral's responses served to remind me that I had seen something similar, that is, something related to Triggers in UWP . Well, the equivalent of TargetedTriggerAction of WPF is an active Window Actives ), of the Behavior category called DataTriggerBehavior This component allows you to create a Trigger that performs an action based on a condition. This component is not available by default, you must install it via NuGet, whose package name is Microsoft.Xaml.Behaviors.Uwp.Managed . On the date of this response version 2.0 presented problems, so I downgraded to version 1.1 that worked normally. The documentation and examples of how to use it is on the link I left, but it is extremely simple. For my problem, it looks like this.

            <ListView x:Name="HamburgerMenuListView1" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">

                <Interactivity:Interaction.Behaviors>
                    <Core:DataTriggerBehavior Binding="{Binding SelectedIndex, ElementName=HamburgerMenuListView1, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="0">
                        <Core:ChangePropertyAction TargetObject="{Binding ElementName=HamburgerMenuListView2}" PropertyName="SelectedIndex" Value="-1"/>
                    </Core:DataTriggerBehavior>
                </Interactivity:Interaction.Behaviors>
            </ListView>


            <ListView x:Name="HamburgerMenuListView2" RelativePanel.AlignBottomWithPanel="True" Margin="0,0,0,20" RelativePanel.AlignLeftWithPanel="True" RelativePanel.AlignRightWithPanel="True">

                <Interactivity:Interaction.Behaviors>
                    <Core:DataTriggerBehavior Binding="{Binding SelectedIndex, ElementName=HamburgerMenuListView2, Mode=OneWay}" ComparisonCondition="GreaterThanOrEqual" Value="0">
                        <Core:ChangePropertyAction TargetObject="{Binding ElementName=HamburgerMenuListView1}" PropertyName="SelectedIndex" Value="-1"/>
                    </Core:DataTriggerBehavior>
                </Interactivity:Interaction.Behaviors>


            </ListView>

It is necessary to configure the namespaces in XAML, in my case it was:

xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"

Well, that's it, let's go to next:)

    
11.01.2017 / 00:47
3

In WPF one possible way is to use a TargetedTriggerAction and Interaction.Triggers .

Add the System.Windows.Interactivity reference to the project.

Type a class that inherits from TargetedTriggerAction :

public class RemoveSelectionTargetAction : TargetedTriggerAction<DependencyObject>
{
    private static bool _canDoAction = true;
    protected override void Invoke(object parameter)
    {
        var selector = TargetObject as Selector;
        if (selector != null)
        {
            if (selector.SelectedIndex == -1) return;
            if (_canDoAction)
            {
                _canDoAction = false;
                selector.SelectedIndex = -1;
            }
            else
            {
                _canDoAction = true;
            }
        }
        else
        {
            throw new 
                InvalidOperationException("Esta acção apenas pode ser aplicada a objectos baseados em Selector");
        }
    }
}

In XAML , where to use it, add the namespace System.Windows.Interactivity and your project (TestWpf, in this example).

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="clr-namespace:TesteWpf"

In the TopListView declaration add:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <local:RemoveSelectionTargetAction TargetObject="{Binding ElementName=BottomListView}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

and in the BottomListView add:

<i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
        <local:RemoveSelectionTargetAction TargetObject="{Binding ElementName=TopListView}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

Notes:

  • selector.SelectedIndex = -1 triggers the event, so it was necessary to use the _canDoAction flag to prevent the code from running again.
  • Only works with two ListViews.
10.01.2017 / 13:27
2

One approach that should work in both WPF and UWP is to use an Attached Property .

Type a class to manage and group the ListView / Selector :

public class GroupManager
{
    public static readonly DependencyProperty IsGroupedProperty = DependencyProperty.RegisterAttached(
         "IsGrouped",
         typeof(bool),
         typeof(GroupManager),
         new FrameworkPropertyMetadata(false, OnIsGroupedChanged));

    public static bool GetIsGrouped(DependencyObject d)
    {
        return (bool)d.GetValue(IsGroupedProperty);
    }

    public static void SetIsGrouped(DependencyObject d, bool value)
    {
        d.SetValue(IsGroupedProperty, value);
    }

    private static void OnIsGroupedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var selector = d as Selector;
        if (selector == null)
        {
            throw new
                InvalidOperationException("Esta propriedade apenas pode ser aplicada a objectos baseados em Selector");
        }
        var isGrouped = (bool)e.NewValue;
        if (isGrouped)
        {
            Register(selector);
            if (selector.SelectedIndex != -1)
            {
                UpdateGroupSelection(selector);
            }
        }
        else
        {
            Unregister(selector);
        }
    }

    private static void Unregister(Selector selector)
    {
        GroupElements.Remove(selector);
        selector.SelectionChanged -= OnSelectionChanged;
    }

    private static void Register(Selector selector)
    {
        selector.SelectionChanged += OnSelectionChanged;
        GroupElements.Add(selector);
    }

    private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        UpdateGroupSelection(sender as Selector);
    }

    private static void UpdateGroupSelection(Selector selector)
    {
        foreach (Selector element in GroupElements)
        {
            if (element != selector)
            {
                element.SelectionChanged -= OnSelectionChanged;
                element.SelectedIndex = -1;
                element.SelectionChanged += OnSelectionChanged;
            }
        }
    }

    private static readonly ArrayList GroupElements = new ArrayList(2);
}

This class declares DependencyPropreterty IsGrouped and is registered as Attached which, when assigned to a ListView , will indicate whether it should be considered in a group in that only one element can have selected items.

Example usage:

....
....
<Grid Margin="0,50,0,0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
        <ColumnDefinition Width="1*"/>
    </Grid.ColumnDefinitions>
    <ListView x:Name="TopListView" local:GroupManager.IsGrouped="True"/>
    <ListView x:Name="BottomListView" Grid.Column="1"  local:GroupManager.IsGrouped="True"/>
    <ListView x:Name="AnotherListView" Grid.Column="2"
              local:GroupManager.IsGrouped="{Binding ElementName=CheckBox, Path=IsChecked}"/>
    <CheckBox x:Name="CheckBox" Grid.Column="3"/>
</Grid>
.....
.....

In this example, AnotherListView only participates in the group when CheckBox is selected.

    
10.01.2017 / 19:20