I posted this on Stack Overflow with no luck, so excuse the cross-post.
I have an app with a Xamarin forms listview that's fed by an ObservableCollection to list user games I fetch from my server. The first time I load it up it works just fine. If I modify that existing ObservableCollection in any way my app crashes with a NullException. I've narrowed it down to my custom ViewCell (GameCell) and I've posted that below. If I comment one line out toward the bottom:
this.SetBinding(IsEnabledProperty, "Enabled");
Then it no longer crashes updating the ObservableCollection, but then I no longer get the functionality to have some cells enabled and some cells disabled. I'm guessing it's trying to set the isEnabled property to a cell that no longer exists because it's been removed on my reload. Any ideas how do this with out crashing? BTW, this works fine on Android, just not on iOS
using System;
using Xamarin.Forms;
using FFImageLoading.Forms;
namespace Upwords
{
public class GameCell : ViewCell
{
public GameCell ()
{
var icon = new CachedImage () {
Aspect = Aspect.AspectFit,
HorizontalOptions = LayoutOptions.Fill,
VerticalOptions = LayoutOptions.Fill,
BackgroundColor = AppStyle.IconColor,
IsVisible = false,
};
icon.SetBinding (CachedImage.SourceProperty, "Icon");
icon.SetBinding (CachedImage.LoadingPlaceholderProperty, "IconPlaceholder");
icon.SetBinding (CachedImage.ErrorPlaceholderProperty, "IconPlaceholder");
icon.SetBinding (CachedImage.WidthRequestProperty, "Height");
icon.SetBinding (CachedImage.HeightRequestProperty, "Height");
icon.Success += (object sender, CachedImageEvents.SuccessEventArgs e) => {
icon.FadeAnimationEnabled = false;
if(icon.Source.GetType() == typeof(Xamarin.Forms.UriImageSource)) {
icon.BackgroundColor = Color.Transparent;
}
};
icon.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(icon.Source != null)
icon.IsVisible = true;
else
icon.IsVisible = false;
};
var nameLabel = new Label () {
FontFamily = "Helvetica",
FontAttributes = FontAttributes.Bold,
FontSize = Device.GetNamedSize (NamedSize.Medium, typeof(Label)),
TextColor = Color.Black,
};
nameLabel.SetBinding (Label.TextProperty, "Name");
//Hide label if it's blank
nameLabel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(nameLabel.Text == "")
nameLabel.IsVisible = false;
else
nameLabel.IsVisible = true;
};
var detailsLabel = new Label () {
FontFamily = "Helvetica",
FontSize = Device.GetNamedSize (NamedSize.Small, typeof(Label)),
FontAttributes = FontAttributes.Bold,
TextColor = Color.FromHex ("#666"),
IsVisible = false,
};
detailsLabel.SetBinding (Label.TextProperty, "Details");
//Hide label if it's blank
detailsLabel.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(string.IsNullOrEmpty( detailsLabel.Text ) )
detailsLabel.IsVisible = false;
else
detailsLabel.IsVisible = true;
};
var textLayout = new StackLayout {
Padding = new Thickness (5, 0, 0, 0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.CenterAndExpand,
Children = { nameLabel, detailsLabel}
};
var optionSwitch = new Switch() {
HorizontalOptions = LayoutOptions.End,
VerticalOptions = LayoutOptions.Center,
IsVisible = false,
};
optionSwitch.SetBinding (Switch.IsVisibleProperty, "IsSwitch");
var statusLayout = new StackLayout {
Padding = new Thickness (10, 10, 10, 10),
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { icon, textLayout, optionSwitch}
};
statusLayout.SetBinding (StackLayout.HeightRequestProperty, "Height");
var separatorBox = new BoxView {
HeightRequest = 1,
HorizontalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.FromRgb(.75,.75,.75),
};
separatorBox.SetBinding (BoxView.HeightRequestProperty, "SeparatorHeight");
var separatorBoxLayout = new StackLayout {
Padding = new Thickness (50, 0, 0, 0),
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = { separatorBox }
};
var cellLayout = new StackLayout {
Spacing = 0,
Padding = new Thickness (0, 0, 0, 0),
Orientation = StackOrientation.Vertical,
HorizontalOptions = LayoutOptions.FillAndExpand,
BackgroundColor = Color.White,
Opacity = 1.0,
Children = { statusLayout, separatorBoxLayout }
};
cellLayout.SetBinding (StackLayout.OpacityProperty, "Opacity");
var headerFrame = new ListViewLabel () {
};
headerFrame.SetBinding (ListViewLabel.IsVisibleProperty, "IsLabel");
headerFrame.SetBinding (ListViewLabel.TextProperty, "Name");
headerFrame.SetBinding (ListViewLabel.HeightRequestProperty, "Height");
headerFrame.PropertyChanged += (object sender, System.ComponentModel.PropertyChangedEventArgs e) => {
if(headerFrame.IsVisible)
cellLayout.IsVisible = false;
else
cellLayout.IsVisible = true;
};
var paddedLayout = new StackLayout {
Spacing = 0,
Orientation = StackOrientation.Horizontal,
HorizontalOptions = LayoutOptions.FillAndExpand,
VerticalOptions = LayoutOptions.FillAndExpand,
Children = { headerFrame, cellLayout }
};
this.SetBinding(IsEnabledProperty, "Enabled");
this.View = paddedLayout;
}
}
public class GameCellData
{
public string Icon { get; set; }
public string IconPlaceholder { get; set; }
public string Name { get; set; }
public string Details { get; set; }
public int Height { get; set; }
public int SeparatorHeight { get; set; }
public bool Enabled { get; set; }
public double Opacity { get; set; }
public bool IsLabel { get; set; }
public bool IsSwitch { get; set; }
}
}