Link selection between two Listviews

Asked

Viewed 96 times

3

It would be possible to link the single selection between two objects ListView via Xaml?

For example, I have two ListView, the TopListView and the BottomListView, both configured for simple selection, ie only one item can be selected. However, as are ListView independent, one can have an item selected in the TopListView at the same time that there is one selected in the BottomListView.

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

I know how to do this via code-Behind, manipulating the event SelectionChanged of each ListView. But I am looking to know if it is possible to do this without having to appeal to the code-Behind.

  • The worst is that not even using the event SelectionChanged is being as simple as I thought it would be. The problem is that defining the property SelectedIndex via code also triggers the event SelectionChanged. I tried to get around using the property FocusState so that he can only make the change if the ListView does not have the focus, but has no effect. code of Event handlers.

  • See help: http://stackoverflow.com/questions/359424/mutually-exclusive-selection-of-two-listviews

  • 1

    In WPF it is easy to do using a Targetedtriggeraction and Interaction.Triggers. I do not know if it is possible to use them in UWP or if there is something equivalent. If you find it useful, tell me I’ll put an answer.

  • @ramaral It will be good, it can help because UWP and WPF share many functionality, think until most.

  • So, for the answer to be valid, I will add the tag WPF to the question.

3 answers

3

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

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

Write a class you inherit 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 the XAML, where to use it, add the namespace System.Windows.Interactivity and your project(Testewpf, in this example).

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

In the Toplistview statement add:

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

and in Bottomlistview add:

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

Notes:

  • selector.SelectedIndex = -1 makes trigger the event, so it was necessary to resort to the flag _canDoAction to prevent the code from being executed again.
  • Only works with two Listview.

2

One approach that should work on both WPF and UWP is to use a Attached Property.

Write a class to manage and group 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);
}

Dependencypropreterty is declared in this class IsGrouped and registered as Attached which, when assigned to a Listview(Selector), indicate whether it should be considered in a group where only one of the elements may have selected items.

Example of use:

....
....
<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 the AnotherListView participates in the group only when the CheckBox is selected.

  • What is wrong in the answer to receive -1?

2


Ramaral’s answers served to remind me that I had seen something similar, that is, something related to Triggers in the UWP. Well, the equivalent of TargetedTriggerAction of WPF is an active element (Actives Window), of the category Behavior 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 need to install it via Nuget, whose package name is Microsoft.Xaml.Behaviors.Uwp.Managed. At the date of this reply 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 in the link I left, but it is extremely simple. For my trouble, it was as follows.

            <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:

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

Well, that’s it, let’s go next time :)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.