I'll start by saying this is going to be a hard one- I might need Xamain direct support.
I have a custom ListView that does item template selecting based on the data type of the source objects
The ListView class is this:
public class MenuItemListView : ListView
{
public static readonly BindableProperty TemplateSelectorProperty = BindableProperty.Create<MenuItemListView, IDataTemplateSelector>(p => p.TemplateSelector, null);
public IDataTemplateSelector TemplateSelector
{
get { return (IDataTemplateSelector)GetValue(TemplateSelectorProperty); }
set { SetValue(TemplateSelectorProperty, value); }
}
public MenuItemListView()
{
ItemTemplate = new DataTemplate(() =>
{
var content = new ViewCell();
content.BindingContextChanged += OnBindingContextChanged;
return content;
});
}
private void OnBindingContextChanged(object sender, EventArgs e)
{
var cell = sender as ViewCell;
if (cell != null && TemplateSelector != null)
{
var template = TemplateSelector.SelectTemplate(cell.BindingContext);
if (template == null)
return;
var viewCell = (ViewCell)template.CreateContent();
viewCell.BindingContext = cell.BindingContext;
cell.View = viewCell.View;
}
}
}
This class uses a TemplateSelector class as follows:
public class DataTemplateSelector : IDataTemplateSelector
{
public DataTemplate BitCellTemplate { get; set; }
public DataTemplate BitEnumCellTemplate { get; set; }
public DataTemplate ButtonCellTemplate { get; set; }
public DataTemplate DateTimeCellTemplate { get; set; }
public DataTemplate EditTextCellTemplate { get; set; }
public DataTemplate PickerCellTemplate { get; set; }
public DataTemplate SelectTemplate(object dataItem)
{
if (dataItem == null)
return null;
if (dataItem is BitItemViewModel)
return BitCellTemplate;
if (dataItem is BitEnumItemViewModel)
return BitEnumCellTemplate;
if (dataItem is ButtonItemViewModel)
return ButtonCellTemplate;
if (dataItem is DateTimeItemViewModel)
return DateTimeCellTemplate;
if (dataItem is EditTextItemViewModel)
return EditTextCellTemplate;
if (dataItem is PickerItemViewModel)
return PickerCellTemplate;
throw new Exception("Unknown Selector");
}
}
The XAML markup is as follows:
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="BitCellTemplate">
<cells:BitItemCell />
</DataTemplate>
<DataTemplate x:Key="BitEnumCellTemplate">
<cells:BitEnumItemCell />
</DataTemplate>
<DataTemplate x:Key="ButtonCellTemplate">
<cells:ButtonItemCell />
</DataTemplate>
<DataTemplate x:Key="DateTimeCellTemplate">
<cells:DateTimeItemCell />
</DataTemplate>
<DataTemplate x:Key="EditTextCellTemplate">
<cells:EditTextItemCell />
</DataTemplate>
<DataTemplate x:Key="PickerCellTemplate">
<cells:PickerItemCell />
</DataTemplate>
<menuItemSelector:DataTemplateSelector x:Key="TableTemplateSelector" BitCellTemplate="{StaticResource BitCellTemplate}"
BitEnumCellTemplate="{StaticResource BitEnumCellTemplate}"
ButtonCellTemplate="{StaticResource ButtonCellTemplate}"
DateTimeCellTemplate="{StaticResource DateTimeCellTemplate}"
EditTextCellTemplate="{StaticResource EditTextCellTemplate}"
PickerCellTemplate="{StaticResource PickerCellTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
<controls:MenuItemListView x:Name="LV" Grid.Column="1" Grid.Row="0" ItemsSource="{Binding MenuItems}" TemplateSelector="{StaticResource TableTemplateSelector}" ItemTapped="OnItemTapped" SeparatorVisibility="None" RowHeight="60" VerticalOptions="FillAndExpand" IsVisible="True" BackgroundColor="Transparent" />
For this question lets assume the type we are concerned with is the ButtonItemCell
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell.View >
<Grid ColumnSpacing="0" RowSpacing="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.2*" />
<ColumnDefinition Width="0.6*" />
<ColumnDefinition Width="0.2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*" />
<RowDefinition Height="0.8*" />
<RowDefinition Height="0.1*" />
</Grid.RowDefinitions>
<Button Grid.Column="1" Grid.Row="1" Text="{Binding ButtonText}" TextColor="{Binding ButtonTextColor}" BackgroundColor="{Binding ButtonBackground}" FontSize="{x:Static style:FontResources.xSmall}" Command="{Binding ClickCommand}" />
</Grid>
</ViewCell.View>
Ok- on to the problem. I populate the ItemSource with an ObservableCollection containing ViewModels for these different element types. So when the TemplateSelector encounters a ButtonItemViewModel object it associates it with the ButtonItemCell. This all works great, I am able to dynamically populate this ListView with all of these different ViewCells depending on the underlying ViewModel.
Where the breakdown occurs is that the Background (and some other) bindings stop working randomly. Actually that isn't correct the binding does fire and I can watch the property return the correct value but the UI element is never updated. Example, in one case the Button background is supposed to be Green. I can watch the property returning Green but the button does not render green. However other binding on the same ViewModel; i.e. Text, TextColor work fine. I have verified that the binding context are getting set correct for each ViewCell. As I said this is random but occurs in a consistent manner meaning that if I go through the same progression the same elements will not work. However there is no difference between the prior one in the list the worked fine or the items after that work. I can have 5 buttons in the list that are all the same and only one will fail. The really strange thing is that if I set the MainPage to some other page and then put this page back in MainPage everything draws correctly (no binding takes place so I know the controls had the right data).
This is happening on the Android, I have not been able to test on the other platforms because this app uses Native c++ libraries that we do not have built for iOS or WinPhone yet.
As you can see the code is pretty straight forward. I really have no idea why the control isn't looking at what was returned from the property.
One other curious thing, lets say the 4th button in a list of 5 is having the problem and I switch the page out and back in causing it to draw correctly. Now the 4th button is green and correct. Well if I go to the next menu (which unloads the collection and repopulates it), now the 4th item in the new list will be green even if it isn't suppose to, Almost like the ListView is holding on to the property data from the ViewCell that has gone away,
I would like to solve this correctly but until then if anyone has a solution or even a workaround to make the page redraw (I've tried Page.ForceLayuot) I would be thrilled.
I'm thinking that this is a problem somewhere inside of the Forms ListVew or even binding manager (I hope not!)
Any help would be EXTREMELY appreciates as my client is breathing down my neck on this one.
Thanks,
Steve