I have this scenario: (NOTE CODE CHANGED)
- I have a user control that has a bindable property DataSource of type ObservableCollection.
- I subscribe to the CollectionChanged event of the Observable collection in my user control.
- I also subscribe to the BindingContextChanged event of the user control.
- When my page hosting the user control loads, I set the bindable property DataSource to an empty ObservableCollection and I set the BindingContext of control to the same thing. The BindingContextChanged event fires in my user control and the property set executes for my DataSource property.
- If I manually add items to my observable collection using the .Add() method, the CollectionChanged event in my user control fires.
- However, if I set the bound observable collection to a new observable collection, no events fire in my user control letting me know the contents of the observable collection changed.
- If I re-set the binding context property of the user control to the new observable collection, the BindingContextChanged event fires, but my bound DataSource property still does not contain the new list data.
The way my actual application works is that I make call to a server and retrieve the current state of the list as it sits on the server, i.e. a complete replace of the existing list.
The challenge now seems to be that the Set of the DataSource property in my usercontrol is never being called. Any ideas?
Here is my code DataGrid XAML
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ObeservableCollectionSample.DataGrid">
<ContentView.Content>
<StackLayout Orientation="Vertical" HorizontalOptions="Center" VerticalOptions="Fill" Spacing="0">
<ScrollView Orientation="Both" HorizontalOptions="Center" VerticalOptions="Fill" Margin="0,0,0,0"
HorizontalScrollBarVisibility="Default" VerticalScrollBarVisibility="Default" Padding="0,0,0,16" >
<Grid x:Name="grdDataGrid" RowSpacing="0" ColumnSpacing="0" IsVisible="false" >
</Grid>
</ScrollView>
<Label x:Name="lblNoDataMessage" HorizontalOptions="Center" VerticalOptions="Start" IsVisible="false"/>
</StackLayout>
</ContentView.Content>
</ContentView>
Here is the code behind for the user control:
using System.Collections.ObjectModel;
using System.Diagnostics;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ObeservableCollectionSample
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DataGrid : ContentView
{
public static readonly BindableProperty DataSourceProperty = BindableProperty.Create(nameof(DataSource), typeof(ObservableCollection<string>), typeof(DataGrid));
public ObservableCollection<string> DataSource
{
get
{
return (ObservableCollection<string>)GetValue(DataSourceProperty);
}
set
{
SetValue(DataSourceProperty, value);
if (value == null)
{
//TODO Clear datagrid
}
else
{
//going to change this to subscribe to the collection changed events
DataSource.CollectionChanged += DataSource_CollectionChanged;
}
}
}
private void DataSource_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine("DataSource_CollectionChanged Event Raised");
}
public DataGrid()
{
InitializeComponent();
this.BindingContextChanged += DataGrid_BindingContextChanged;
}
private void DataGrid_BindingContextChanged(object sender, System.EventArgs e)
{
Debug.WriteLine("DataGrid_BindingContextChanged Event Raised");
}
}
}
Then my test page simply contains the user control plus some button to test changing the collection:
Test Page XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:MyDataGrid="clr-namespace:ObeservableCollectionSample;assembly=ObeservableCollectionSample"
x:Class="ObeservableCollectionSample.MainPage">
<StackLayout>
<StackLayout Orientation="Vertical" HorizontalOptions="Fill" VerticalOptions="StartAndExpand">
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="Welcome to Xamarin.Forms!" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<MyDataGrid:DataGrid x:Name="grdData" VerticalOptions="FillAndExpand" HorizontalOptions="Center"
DataSource="{Binding DataList}"/>
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="Center" VerticalOptions="End">
<Button x:Name="btnAdd1" Text="Add List 1" HorizontalOptions="Center" VerticalOptions="Center" Clicked="btnAdd1_Clicked"/>
<Button x:Name="btnAdd2" Text="Add List 2" HorizontalOptions="Center" VerticalOptions="Center" Clicked="btnAdd2_Clicked"/>
</StackLayout>
</StackLayout>
</ContentPage>
Test page code behind:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace ObeservableCollectionSample
{
public partial class MainPage : ContentPage
{
private ObservableCollection<string> iobj_DataList = new ObservableCollection<string>();
public ObservableCollection<string> DataList
{
get
{
return iobj_DataList;
}
set
{
iobj_DataList= value;
NotifyPropertyChanged();
}
}
public MainPage()
{
InitializeComponent();
grdData.BindingContext = this;
}
private void btnAdd1_Clicked(object sender, EventArgs e)
{
iobj_DataList.Add("String 1");
iobj_DataList.Add("String 2");
iobj_DataList.Add("String 3");
NotifyPropertyChanged("DataList");
}
private void btnAdd2_Clicked(object sender, EventArgs e)
{
List<string> myList = new List<string>();
myList.Add("String 4");
myList.Add("String 5");
myList.Add("String 6");
//I would like to have this line raise the needed events in my control
DataList = new ObservableCollection<string>(new ObservableCollection<string>(myList));
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}