Forum Xamarin.Forms

ListView items visibility not working correctly

FraukeNonnenmacherFraukeNonnenmacher USMember
edited November 2016 in Xamarin.Forms

I have a ListView, bound to an ObservableCollection, with a template that contains views that are made visible/invisible through user interaction. However, the visibility switching does not work as expected - items that were initially invisible stay invisible while some items that were initially visible can be switched back and forth correctly. I'm stumped as to where things are going wrong.

Each row of the ListView has a three column grid. The first column contains two labels, which have their visibility toggled, i.e. only one should be visible at any given time. The second column contains a label with the same text as the label that should be visible. The third column contains a button that toggles the visibility of the labels in column one.

The page renders fine on loading on both Windows and Android platforms.

On UWP, after pressing the toggle button, all text content in that row vanishes, including the text of the button! Pressing the toggle button again makes the text in the first column (that was visible initially) reappear, but all other text stays invisible.

On Win8 it's much the same, except that the button text remains.

On Android, the toggle works fine until an item is selected and deleted. After that, with the exception of the last one and any item that has been toggled before the deletion, the first column goes blank when toggled to display the second label, while the second column behaves correctly. So if, for example, I toggle Baz, then delete Bar, the following happens: Foo, Baz and Kevin behave as I would expect, but Dave will toggle between Dave1 and nothing in the first column.

This behaviour has me totally stumped, so if anybody here has any idea as to what's going on, I'd be extremely grateful.

I'm using MVVMLight, and here's my code:

View:

<StackLayout>
  <ListView ItemsSource="{Binding TestList}"
            SelectedItem="{Binding SelectedItem}">
    <ListView.RowHeight>
      <OnPlatform x:TypeArguments="x:Int32" iOS="90" Android="90" WinPhone="100" />
    </ListView.RowHeight>
    <ListView.ItemTemplate>
      <DataTemplate>
        <ViewCell>
          <ContentView Padding="5">
            <Grid HorizontalOptions="FillAndExpand">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
              </Grid.ColumnDefinitions>
              <Label Text="{Binding Text1}" IsVisible="{Binding Text1Visible}"/>
              <Label Text="{Binding Text2}" IsVisible="{Binding Text2Visible}"/>
              <Label Text="{Binding CurrentText}" Grid.Column="1"/>
              <Button Grid.Column="2"
                      Text="Toggle"
                      Command="{Binding Source={x:Reference MainPage}, Path=BindingContext.ToggleCommand}"
                      CommandParameter="{Binding}"/>
            </Grid>
          </ContentView>
        </ViewCell>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
  <Button Text="Delete selected" Command="{Binding DeleteCommand}"/>
</StackLayout>

ViewModel:

public class MainViewModel : ViewModelBase
{
    public ObservableCollection<MyClass> TestList { get; set; }
    public MyClass SelectedItem { get; set; }
    public RelayCommand<MyClass> ToggleCommand { get; set; }
    public RelayCommand DeleteCommand { get; set; }

    public MainViewModel()
    {
        ToggleCommand = new RelayCommand<MyClass>(ToggleClicked);
        DeleteCommand = new RelayCommand(DeleteSelected);
        TestList = new ObservableCollection<MyClass>();
        TestList.Add(new MyClass { Text1 = "Foo1", Text2 = "Foo2", Text1Visible = true });
        TestList.Add(new MyClass { Text1 = "Bar1", Text2 = "Bar2", Text1Visible = true });
        TestList.Add(new MyClass { Text1 = "Baz1", Text2 = "Baz2", Text1Visible = true });
        TestList.Add(new MyClass { Text1 = "Dave1", Text2 = "Dave2", Text1Visible = true });
        TestList.Add(new MyClass { Text1 = "Kevin1", Text2 = "Kevin2", Text1Visible = true });
    }

    private void DeleteSelected()
    {
        if (SelectedItem != null)
            TestList.Remove(SelectedItem);
    }

    private void ToggleClicked(MyClass myclass)
    {
        myclass.Text2Visible = myclass.Text1Visible;
        myclass.Text1Visible = !myclass.Text1Visible;
    }
}

Model:

public class MyClass : INotifyPropertyChanged
{
    private bool _t1v;
    private bool _t2v;
    private string _text1;
    private string _text2;
    private string _currentText;

    public string Text1
    {
        get { return _text1; }
        set
        {
            _text1 = value;
            RaisePropertyChanged(nameof(Text1));
        }
    }

    public string Text2
    {
        get { return _text2; }
        set
        {
            _text2 = value;
            RaisePropertyChanged(nameof(Text2));
        }
    }

    public string CurrentText
    {
        get { return _currentText; }
        set
        {
            _currentText = value;
            RaisePropertyChanged(nameof(CurrentText));
        }
    }

    public bool Text1Visible
    {
        get { return _t1v; }
        set
        {
            _t1v = value;

            // Set the current text to the one that should be visible
            if (_t1v)
                CurrentText = Text1;
            else
                CurrentText = Text2;

            RaisePropertyChanged(nameof(Text1Visible));
        }
    }

    public bool Text2Visible
    {
        get { return _t2v; }
        set
        {
            _t2v = value;

            // Set the current text to the one that should be visible
            if (_t2v)
                CurrentText = Text2;
            else
                CurrentText = Text1;

            RaisePropertyChanged(nameof(Text2Visible));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Sign In or Register to comment.