How to Bind IsSelected Property in DataTemplate of ListViewItem
Asked Answered
H

3

8

Well, I want to make the ListViewItem in UWP ,that'll be changing his view on selecting. So I need to change the Visibility property of some elements of ListViewItem on selecting.

I found some way to do this with making custom Style of ListViewItem and Binding IsSelected property like this:

<Style x:Key="VehicleListViewItemStyle" TargetType="ListViewItem" >
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="ListViewItem">
                        <Grid Background="Gray" Margin="1">
                            <Border Margin="2" Padding="10" Background="Gray" >
                                <StackPanel>
                                    <ContentPresenter x:Name="Presenter1" />
                                    <StackPanel Orientation="Horizontal" Background="Transparent" Margin="-10,0,-9,-9" VerticalAlignment="Center" x:Name="infoPanel"
                                        Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}">
                                        <TextBlock Text="{Binding  DeviceID}/>                                                                            </StackPanel>
                                </StackPanel>
                            </Border>
                            <Border BorderThickness="1" BorderBrush="Orange" Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}"/>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>  

Its working good, but with this way I cant bind the DeviceID text.

Another one way is creating DataTemplate like this:

<DataTemplate x:Key="monitoringListViewItem" x:Name="item">
            <Grid Background="Gray" Margin="1" Width="300" >
                <StackPanel>
                    <ContentPresenter x:Name="Presenter"/>
                    <StackPanel Orientation="Horizontal">
                        <Image Source="/Assets/14th_crane_stop.png" Height="50" Width="50" Stretch="Uniform"/>
                        <StackPanel Orientation="Vertical" Margin="25,0,0,0 " 
                                    Visibility="{Binding IsSelected, RelativeSource={RelativeSource Mode=TemplatedParent}, Converter={StaticResource BooleanToVisibilityConverter}}"
                                    >
                            <TextBlock Text="{Binding DeviceID}" Style="{StaticResource VehicleTextStyle}"/>
                            <TextBlock Text="{Binding Mark}" Style="{StaticResource VehicleTextStyle}"/>
                        </StackPanel>
                    </StackPanel>
                </StackPanel >
            </Grid>
        </DataTemplate>

Now I can bind the text correctly, but cant bind the IsSelected property. I've tried to do this with different Modes, but it still doesn't work because I cant use the TemplatedParent key inside the DataTemplate.

So I need some answers:

-can I bind the text in first way and how can I do that? -how can I bind the IsSelected property in the second way?

Hyperkeratosis answered 30/10, 2016 at 10:3 Comment(2)
Does your ListView have single selection or multiple selection?Voletta
@Decade Moon, MultipleHyperkeratosis
V
4

I don't recommend changing the ListViewItem template since you lose all the bells and whistles it provides (selection appearance, ability to be checked, etc).

Using Mode=TemplatedParent in the second snippet won't work because the templated parent from that context is a ListViewItemPresenter, not the ListViewItem (which is the presenter's parent).

It looks like what you're trying to do is to show additional information in the list item when it is selected.

Single selection

C# classes

public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected bool SetProperty<T>(ref T property, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(property, value))
        {
            return false;
        }

        property = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

public class Item : NotifyPropertyChangedBase
{
    private string text;
    public string Text
    {
        get { return text; }
        set { SetProperty(ref text, value); }
    }

    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set { SetProperty(ref isSelected, value); }
    }
}

public class MainPageViewModel : NotifyPropertyChangedBase
{
    public List<Item> Items { get; set; }

    private Item selectedItem;
    public Item SelectedItem
    {
        get { return selectedItem; }
        set
        {
            if (selectedItem != value)
            {
                if (selectedItem != null)
                {
                    selectedItem.IsSelected = false;
                }

                SetProperty(ref selectedItem, value);

                if (selectedItem != null)
                {
                    selectedItem.IsSelected = true;
                }
            }
        }
    }

    public MainPageViewModel()
    {
        Items = new List<Item>()
        {
            new Item() { Text = "Apple" },
            new Item() { Text = "Banana" },
        };
    }
}

MainPage.xaml

<ListView ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:Item">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding Text}"/>

                <!-- x:Bind doesn't require visibility converter if min SDK is targeting Anniversary update -->
                <TextBlock Text="I'm selected!" Grid.Column="1" Visibility="{x:Bind IsSelected, Mode=OneWay}"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        DataContext = new MainPageViewModel();
    }
}

Screenshot

Multiple selection

Same as single selection but with the following changes:

MainPageViewModel

public class MainPageViewModel : NotifyPropertyChangedBase
{
    public List<Item> Items { get; set; }

    public MainPageViewModel()
    {
        Items = new List<Item>()
        {
            new Item() { Text = "Apple" },
            new Item() { Text = "Banana" },
        };
    }

    public void SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        foreach (Item item in e.RemovedItems)
        {
            item.IsSelected = false;
        }

        foreach (Item item in e.AddedItems)
        {
            item.IsSelected = true;
        }
    }
}

MainPage.xaml

<ListView ItemsSource="{Binding Items}" SelectionChanged="{x:Bind ViewModel.SelectionChanged}" SelectionMode="Extended">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
        </Style>
    </ListView.ItemContainerStyle>
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="local:Item">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>

                <TextBlock Text="{Binding Text}"/>
                <TextBlock Text="I'm selected!" Grid.Column="1" Visibility="{x:Bind IsSelected, Mode=OneWay}"/>
            </Grid>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

MainPage.xaml.cs

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        DataContext = new MainPageViewModel();
    }

    public MainPageViewModel ViewModel => (MainPageViewModel)DataContext;
}

Screenshot

Voletta answered 30/10, 2016 at 11:22 Comment(5)
I'm using multiple selection... Im not sure, but with this solution all my selected items'll change VisibleProperty , isn't it?Hyperkeratosis
Yes, the "I'm selected!" TextBlock will show/hide automatically for each item as the selection changes (thanks to INotifyPropertyChanged support). I've edited my answer to show a solution for multiple selection.Voletta
it works pretty good, but I hoped find some way only in XAML.... anyway, thank you!Hyperkeratosis
Unfortunately RelativeSource is very limited in UWP, unlike WPF. FindAncestor would be the best solution but it's not supported in UWP, so your options are quite limited.Voletta
Thank you for this detailed answer! According to the current state of technology, is that the only solution or is it possible in the meantime to solve the whole thing without an additional "isSelected" property?Denison
I
2

To answer the actual question in the topic: Yes you can bind ListViewItem.IsSelected quite easily. Make sure your DataTemplate content is wrapped in a ListViewItem.

<ListView.ItemTemplate>
    <DataTemplate x:DataType="local:MyItemViewModel">
        <ListViewItem IsSelected="{Binding IsSelected, Mode=TwoWay}">
            <Grid>
                // template content
            </Grid>
        </ListViewItem>
    </DataTemplate>
</ListView.ItemTemplate>

Note that there are numerous Q&As here for WPF saying this does not work. It does work with UWP (at least as of SDK 10.0.19041). The WPF answers suggest binding it in the ItemsPanelTemplate or in a ResourceDictionary. For some reason this does not work in UWP.

Ilan answered 5/10, 2020 at 12:12 Comment(1)
I have no idea why this isn't marked as the correct answer.Trimmer
M
-1

I did the following and it worked fine

<ListView.ItemTemplate>
    <DataTemplate x:DataType="local:PersonalProfile">
        <ListViewItem x:Name="root">
             <Grid >
             <!-- .... -->
                 <CheckBox IsChecked="{Binding ElementName=root, Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
             </Grid>
        </ListViewItem>
    </DataTemplate>
</ListView.ItemTemplate>
Millenary answered 17/7, 2022 at 20:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.