Ian
Ian

Reputation: 161

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!

Upvotes: 1

Views: 259

Answers (2)

Ian
Ian

Reputation: 161

This is how it ended up looking in the end. Does this seem clunky to anyone else? It would be nice if the ListView control made it easier to deal with collections and SelectionMode="Multiple".

SettingsPage.xaml

<ListView
    x:Name="previewColumnListView"
    ItemsSource="{x:Bind ViewModel.CurrentDrillHole.Collar.Columns, Mode=TwoWay}"
    Height="400"
    SelectionChanged="previewColumnListView_SelectionChanged"
    IsItemClickEnabled="True"
    ItemClick="previewColumnListView_ItemClick">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="models:Column">
            <StackPanel Orientation="Horizontal">
                <CheckBox Click="previewColumnListView_CheckBox_Click" 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>

SettingsPage.xaml.cs

private async void previewColumnListView_CheckBox_Click(object sender, RoutedEventArgs e)
{
    // update the list of selected columns
    Settings.CollarPreviewFields = ViewModel.CurrentDrillHole.Collar.Columns.Where(x => x.Selected).Select(x => x.name).ToList();

    await App.SaveSettings();
}

private void previewColumnListView_ItemClick(object sender, ItemClickEventArgs e)
{
    Column selectedColumn = (Column)e.ClickedItem;

    selectedColumn.Selected = !selectedColumn.Selected;

    // trigger checkbox click event. will update the list and save.
    previewColumnListView_CheckBox_Click(null, null);
}

enter image description here

Upvotes: 0

Roy Li - MSFT
Roy Li - MSFT

Reputation: 8681

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>

Upvotes: 1

Related Questions