C# – listbox isSelected databinding in DataTemplate

cdata-bindingdatatemplatewpf-controlsxaml

I try to simply databind IsSelected property with IsSelected field in my class. But after I change the value in code, it doesn't change the property, neither does clicking on ListBoxItem change the field value.

XAML:

<FlipView ItemsSource="{Binding Source={StaticResource itemsViewSource}}" ... >
    <FlipView.ItemTemplate>
        <DataTemplate>
            <UserControl Loaded="StartLayoutUpdates" 
                Unloaded="StopLayoutUpdates">
                <!-- other controls -->
                <ListBox Grid.Row="1" Grid.ColumnSpan="3"
                    SelectionMode="Multiple" VerticalAlignment="Center" 
                    ItemsSource="{Binding Answers}">
                    <ListBox.Resources>
                        <local:LogicToText x:Key="logToText" />
                    </ListBox.Resources>

                     <!-- bind IsSelected only in one way from 
                         code to content --> 
                     <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <ListBoxItem 
                              IsSelected="{Binding IsSelected, Mode=TwoWay, Converter={StaticResource logToText}}" 
                              Content="{Binding IsSelected, Mode=TwoWay, Converter={StaticResource logToText}}">

                            </ListBoxItem>

                        </DataTemplate>
                    </ItemsControl.ItemTemplate>


                    <!-- not working at all
                    <ListBox.Resources>
                        <Style TargetType="ListBoxItem">
                            <Setter Property="IsSelected" 
                                Value="{Binding IsSelected, Mode=TwoWay}"/>
                            <Setter Property="Content" 
                                Value="{Binding IsSelected, Mode=TwoWay}"/>
                        </Style>
                    </ListBox.Resources>-->

                </ListBox>
            </UserControl>
        </DataTemplate>
    </FlipView.ItemTemplate>
</FlipView>

Code:

Answers

private ObservableCollection<PrawoJazdyDataAnswer> _answers = 
    new ObservableCollection<PrawoJazdyDataAnswer>();
public ObservableCollection<PrawoJazdyDataAnswer> Answers 
{ 
    get 
    { 
       return this._answers; 
    }  
}    

Single item(Answer)

public class PrawoJazdyDataAnswer : NPCHelper// PrawoJazdy.Common.BindableBase
{
    public PrawoJazdyDataAnswer(String ans, bool ansb)
    {
        this._ans = ans;
        this._isSelected = ansb;
    }

    public override string ToString() 
    { 
        return _isSelected.ToString();  //Only For debug purposes 
                                        //normally return _ans 
    }
    private string _ans;
    public string Ans
    {
        get { return this._ans; }
        //set { this.SetProperty(ref this._ans, value); }
    }

    private bool _isSelected;
    public bool IsSelected
    {
        get { return this._isSelected; }
        set
        {
            _isSelected = value;
            FirePropertyChanged("IsSelected");
            //this.SetProperty(ref this._isSelected, value); 
        }
    }
}

FirePropertyChanged

public class NPCHelper : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void FirePropertyChanged(string prop)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }
}

Converter(which sometimes seems to be needed and others not…, I tried ~10 approaches from different tutorials/examples)

public class LogicToText : IValueConverter
{
    /// <summary>
    /// 
    /// </summary>
    public object Convert(object value, Type targetType, 
                          object parameter, string language)
    {
        //if (value == null || (bool)value == false)
          //  return "False";

        return value.ToString();
    }

    /// <summary>
    /// 
    /// </summary>
    public object ConvertBack(object value, Type targetType, 
                              object parameter, string language)
    {
        return value.ToString().Contains("True") ? true : false;
    }

Thanks in advance, and sorry for my English(still learning).

@edit
Thanks for quick reply.

For test purposes I created a button and text block:

<Button Click="spr" >Sprawdź</Button>
<TextBlock Text="{Binding Answers[0].IsSelected, Mode=TwoWay}" > </TextBlock> 

It's in other controls part (above list box, but in FlipView)
Click method

private void spr(object sender, RoutedEventArgs e)
    {
        var ans = ((PrawoJazdyDataQuestion)this.flipView.SelectedItem).Answers;
        foreach (var item in ans)
            item.IsSelected = item.IsSelected ? false : true;
    }

As I wrote, when i'm changing data from code side, it is changing content of element, but not appearance of ListBoxItem. And if I just select it on ListBox, it doesn't change the data in TextBlock neither in ListBox itself.

@edit2 fixing typos…

Best Answer

To change the IsSelected property of the ListBoxItem, you need to change the ListBox.ItemContainerStyle. See here:

<ListBox Grid.Row="1" Grid.ColumnSpan="3"
                SelectionMode="Multiple" VerticalAlignment="Center" 
                ItemsSource="{Binding Answers}">
    <ListBox.ItemContainerStyle>
        <Style TargetType="ListBoxItem">
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
        </Style>
    </ListBox.ItemContainerStyle>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding IsSelected}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Since the binding mode is TwoWay, selecting and deselecting the ListBoxItem's dynamically changes the content of the items to display either True or False.

Also notice how I changed the ListBox.ItemTemplate into a TextBlock instead of a ListBoxItem. The ItemTemplate defines the content of the ListBoxItem, so some type of content control is typically used. Below is the UI structure for the different layouts (this can be viewed using the WPF Tree Visualizer).

ListBoxItem as the ItemTemplate:

enter image description here

TextBlock as the ItemTemplate:

enter image description here

Edit

Also note that I removed the IValueConverter. Since your source and target properties are both bool, there is no need for a converter in this case. Try removing the converter references to see if that fixes the problem.

Related Topic