Search code examples
xamllistviewuwplistviewitem

Binding IsSelected on ListViewItem not working for me in UWP application


I'm working with a list of objects that have a Selected property and I'm trying to bind it to the IsSelected property on ListViewItem within a multi-select ListView control in UWP.

I just can't seem to get the binding to work. The checkboxes in the ListView do not render checked if Selected = True and the Set on Selected never gets triggered when checking an item.

enter image description here

SettingsPage.xaml

<Page.Resources>
    <DataTemplate x:Key="PreviewColumnTemplate" x:DataType="models:Column">
        <TextBlock>
            <Run Text="{x:Bind name}"/>
            <Run Text=" ("/>
            <Run Text="{x:Bind ColumnValidation.column_label}"/>
            <Run Text=") "/>
        </TextBlock>
    </DataTemplate>

    <Style x:Key="previewColumnListViewItem" TargetType="ListViewItem">
        
    </Style>
</Page.Resources>
        
<ListView
    x:Name="previewColumnListView"
    ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"
    ItemTemplate="{StaticResource PreviewColumnTemplate}"
    Height="400"
    SelectionMode="Multiple"
    SelectionChanged="previewColumnListView_SelectionChanged">
    <ListView.Resources>
        <Style TargetType="ListViewItem" BasedOn="{StaticResource previewColumnListViewItem}">
            <Setter Property="IsSelected" Value="{Binding Selected, Mode=TwoWay}"/>
        </Style>
    </ListView.Resources>
</ListView>

The ViewModel.CurrentDrillHole.Collar object is of type Table and looks like so:

public class Table : BindableBase
{
    public string Name { get; set; }
    public TableValidation TableValidation { get; set; }
    public List<Column> Columns { get; set; }
    public List<Row> Rows { get; set; } = new List<Row>();
}

And the Column object looks like so. It is here I want to bind to the Selected property.

public class Column : BindableBase, INotifyPropertyChanged
{
    public string name { get; set; }
    public ColumnValidation ColumnValidation { get; set; }
    public List<RefEntryValue> LookupValues { get; set; } = null;

    private bool _selected = false;

    public bool Selected {
        get => _selected;

        set
        {
            _selected = value;

            OnPropertyChanged();
        }
    }
}

Any ideas of things I can try would be greatly appreciated. Thanks for the help!


Solution

  • When you set SelectionMode="Multiple", ListViewItem uses the default ListViewItemTemplate whose key is "ListViewItemExpanded".

    Its style is as follows:

     <Style TargetType="ListViewItem" x:Key="ListViewItemExpanded">
        ......
        <ControlTemplate TargetType="ListViewItem">
            <Grid x:Name="ContentBorder"
              Control.IsTemplateFocusTarget="True"
              FocusVisualMargin="{TemplateBinding FocusVisualMargin}"
              Background="{TemplateBinding Background}"
              BorderBrush="{TemplateBinding BorderBrush}"
              BorderThickness="{TemplateBinding BorderThickness}"
              CornerRadius="{TemplateBinding CornerRadius}"
              RenderTransformOrigin="0.5,0.5">
                ……
                <Border x:Name="MultiSelectSquare"
                            BorderBrush="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                            BorderThickness="2"
                            Width="20"
                            Height="20"
                            Margin="12,0,0,0"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            Visibility="Collapsed">
                    <Border.Clip>
                        <RectangleGeometry Rect="0,0,20,20">
                            <RectangleGeometry.Transform>
                                <TranslateTransform x:Name="MultiSelectClipTransform" />
                            </RectangleGeometry.Transform>
                        </RectangleGeometry>
                    </Border.Clip>
                    <Border.RenderTransform>
                        <TranslateTransform x:Name="MultiSelectCheckBoxTransform" />
                    </Border.RenderTransform>
                    <FontIcon x:Name="MultiSelectCheck"
                                FontFamily="{ThemeResource SymbolThemeFontFamily}"
                                Glyph="&#xE73E;"
                                FontSize="16"
                                Foreground="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                                Visibility="Collapsed"
                                Opacity="0" />
                </Border>
                <Border x:Name="MultiArrangeOverlayTextBorder"
                            Opacity="0"
                            IsHitTestVisible="False"
                            Margin="12,0,0,0"
                            MinWidth="20"
                            Height="20"
                            VerticalAlignment="Center"
                            HorizontalAlignment="Left"
                            Background="{ThemeResource SystemControlBackgroundAccentBrush}"
                            BorderThickness="2"
                            BorderBrush="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
                    <TextBlock x:Name="MultiArrangeOverlayText"
                  Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=TemplateSettings.DragItemsCount}"
                  Style="{ThemeResource CaptionTextBlockStyle}"
                  IsHitTestVisible="False"
                  Opacity="0"
                  VerticalAlignment="Center"
                  HorizontalAlignment="Center"
                  AutomationProperties.AccessibilityView="Raw" />
                </Border>
    
            </Grid>
    
        </ControlTemplate>
        </Setter.Value>
    </Style>
    

    As you can see, there is no CheckBox in its style, it is composed by Border and FontIcon.

    If you want to solve this problem, I suggest that you could add CheckBox to DataTemplate. By doing this, we can bind “Selected’ to the “IsChecked” property of the CheckBox. Please refer to the following code.

    <ListView
    x:Name="previewColumnListView"
    ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"   
    Height="400"
    SelectionChanged="previewColumnListView_SelectionChanged">
        <ListView.ItemTemplate>
            <DataTemplate x:Key="PreviewColumnTemplate" x:DataType="models:Column">
                <StackPanel Orientation="Horizontal">
                    <CheckBox IsChecked="{Binding Selected, Mode=TwoWay}"/>
                    <TextBlock>
              <Run Text="{x:Bind name}"/>
              <Run Text=" ("/>
              <Run Text="{x:Bind ColumnValidation.column_label}"/>
              <Run Text=") "/>
                    </TextBlock>
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>