Custom control with ItemsSource bound to ObservableCollection in view model not updating upon...





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ height:90px;width:728px;box-sizing:border-box;
}







0















I have a PIN field on a page. The PIN field is implemented with a custom class derived from stack layout, which adds item-source binding capabilities. Its item source is bound to an ObservableCollection of characters in my view-model. The issue I'm experiencing is as the title states, the pin field doesn't update upon adding, deleting from the ObservableCollection.



I've read posts with similar issues to mine. All of their solutions state to ensure that the ObservableCollection property notifies its property changed through the INotifyPropertyChanged interface call. I did this and it still isn't updating the GUI. Please help!



Here is the code:



The xaml for the PIN field



<utility:BindableStackLayout HeightRequest="40"
Orientation="Horizontal"
HorizontalOptions="Center"
ItemsSource="{Binding Pin}">
<utility:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<skia:SKCanvasView PaintSurface="OnPaintSurfacePinDigit"/>
</DataTemplate>
</utility:BindableStackLayout.ItemDataTemplate>
</utility:BindableStackLayout>


SignInPage.xaml.cs



using System;
using MNPOS.ViewModel;
using Xamarin.Forms;
using SkiaSharp.Views.Forms;
using SkiaSharp;

namespace MNPOS.View
{
public partial class SignInPage : CustomNavigationDetailPage
{
public SignInPage()
{
BindingContext = _viewModel;
InitializeComponent();
}

public void OnPaintSurfacePinDigit(object sender, SKPaintSurfaceEventArgs e)
{
...
}

private SignInViewModel _viewModel = new SignInViewModel();
}
}


SignInViewModel



using System;
using System.Text;
using System.Collections;
using MNPOS.Configuration;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace MNPOS.ViewModel
{
public class SignInViewModel : ViewModel
{
public SignInViewModel()
{
_appendDigitCommand = new Command<string>(AppendDigit);
_clearDigitCommand = new Command(ClearDigit);
_signInCommand = new Command(SignIn);
}

public void AppendDigit(string entry)
{
if (_pin.Count < Constants.MaximumPinLength)
{
_pin.Add(entry[0]);
}
}

public void ClearDigit()
{
if (_pin.Count > 0)
{
_pin.RemoveAt(Pin.Count - 1);
}
}

public void SignIn()
{

}

public Command AppendDigitCommand => _appendDigitCommand;
public Command ClearDigitCommand => _clearDigitCommand;
public Command SignInCommand => _signInCommand;
public ObservableCollection<char> Pin
{
get { return _pin; }
set
{
SetProperty<ObservableCollection<char>>(ref _pin, value, nameof(Pin));
}
}

private readonly Command _appendDigitCommand;
private readonly Command _clearDigitCommand;
private readonly Command _signInCommand;
private ObservableCollection<char> _pin = new ObservableCollection<char>();
}
}


ViewModel



using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MNPOS.ViewModel
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((propertyName)));
}

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);

return true;
}
}


}



BindableStackLayout



using System.Collections;
using Xamarin.Forms;

namespace MNPOS.View.Utility
{
public class BindableStackLayout : StackLayout
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());

public DataTemplate ItemDataTemplate
{
get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }
set { SetValue(ItemDataTemplateProperty, value); }
}
public static readonly BindableProperty ItemDataTemplateProperty =
BindableProperty.Create(nameof(ItemDataTemplate), typeof(DataTemplate), typeof(BindableStackLayout));

void PopulateItems()
{
if (ItemsSource == null) return;
foreach (var item in ItemsSource)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}
}
}









share|improve this question























  • an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

    – Jason
    Oct 29 '18 at 0:02


















0















I have a PIN field on a page. The PIN field is implemented with a custom class derived from stack layout, which adds item-source binding capabilities. Its item source is bound to an ObservableCollection of characters in my view-model. The issue I'm experiencing is as the title states, the pin field doesn't update upon adding, deleting from the ObservableCollection.



I've read posts with similar issues to mine. All of their solutions state to ensure that the ObservableCollection property notifies its property changed through the INotifyPropertyChanged interface call. I did this and it still isn't updating the GUI. Please help!



Here is the code:



The xaml for the PIN field



<utility:BindableStackLayout HeightRequest="40"
Orientation="Horizontal"
HorizontalOptions="Center"
ItemsSource="{Binding Pin}">
<utility:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<skia:SKCanvasView PaintSurface="OnPaintSurfacePinDigit"/>
</DataTemplate>
</utility:BindableStackLayout.ItemDataTemplate>
</utility:BindableStackLayout>


SignInPage.xaml.cs



using System;
using MNPOS.ViewModel;
using Xamarin.Forms;
using SkiaSharp.Views.Forms;
using SkiaSharp;

namespace MNPOS.View
{
public partial class SignInPage : CustomNavigationDetailPage
{
public SignInPage()
{
BindingContext = _viewModel;
InitializeComponent();
}

public void OnPaintSurfacePinDigit(object sender, SKPaintSurfaceEventArgs e)
{
...
}

private SignInViewModel _viewModel = new SignInViewModel();
}
}


SignInViewModel



using System;
using System.Text;
using System.Collections;
using MNPOS.Configuration;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace MNPOS.ViewModel
{
public class SignInViewModel : ViewModel
{
public SignInViewModel()
{
_appendDigitCommand = new Command<string>(AppendDigit);
_clearDigitCommand = new Command(ClearDigit);
_signInCommand = new Command(SignIn);
}

public void AppendDigit(string entry)
{
if (_pin.Count < Constants.MaximumPinLength)
{
_pin.Add(entry[0]);
}
}

public void ClearDigit()
{
if (_pin.Count > 0)
{
_pin.RemoveAt(Pin.Count - 1);
}
}

public void SignIn()
{

}

public Command AppendDigitCommand => _appendDigitCommand;
public Command ClearDigitCommand => _clearDigitCommand;
public Command SignInCommand => _signInCommand;
public ObservableCollection<char> Pin
{
get { return _pin; }
set
{
SetProperty<ObservableCollection<char>>(ref _pin, value, nameof(Pin));
}
}

private readonly Command _appendDigitCommand;
private readonly Command _clearDigitCommand;
private readonly Command _signInCommand;
private ObservableCollection<char> _pin = new ObservableCollection<char>();
}
}


ViewModel



using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MNPOS.ViewModel
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((propertyName)));
}

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);

return true;
}
}


}



BindableStackLayout



using System.Collections;
using Xamarin.Forms;

namespace MNPOS.View.Utility
{
public class BindableStackLayout : StackLayout
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());

public DataTemplate ItemDataTemplate
{
get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }
set { SetValue(ItemDataTemplateProperty, value); }
}
public static readonly BindableProperty ItemDataTemplateProperty =
BindableProperty.Create(nameof(ItemDataTemplate), typeof(DataTemplate), typeof(BindableStackLayout));

void PopulateItems()
{
if (ItemsSource == null) return;
foreach (var item in ItemsSource)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}
}
}









share|improve this question























  • an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

    – Jason
    Oct 29 '18 at 0:02














0












0








0








I have a PIN field on a page. The PIN field is implemented with a custom class derived from stack layout, which adds item-source binding capabilities. Its item source is bound to an ObservableCollection of characters in my view-model. The issue I'm experiencing is as the title states, the pin field doesn't update upon adding, deleting from the ObservableCollection.



I've read posts with similar issues to mine. All of their solutions state to ensure that the ObservableCollection property notifies its property changed through the INotifyPropertyChanged interface call. I did this and it still isn't updating the GUI. Please help!



Here is the code:



The xaml for the PIN field



<utility:BindableStackLayout HeightRequest="40"
Orientation="Horizontal"
HorizontalOptions="Center"
ItemsSource="{Binding Pin}">
<utility:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<skia:SKCanvasView PaintSurface="OnPaintSurfacePinDigit"/>
</DataTemplate>
</utility:BindableStackLayout.ItemDataTemplate>
</utility:BindableStackLayout>


SignInPage.xaml.cs



using System;
using MNPOS.ViewModel;
using Xamarin.Forms;
using SkiaSharp.Views.Forms;
using SkiaSharp;

namespace MNPOS.View
{
public partial class SignInPage : CustomNavigationDetailPage
{
public SignInPage()
{
BindingContext = _viewModel;
InitializeComponent();
}

public void OnPaintSurfacePinDigit(object sender, SKPaintSurfaceEventArgs e)
{
...
}

private SignInViewModel _viewModel = new SignInViewModel();
}
}


SignInViewModel



using System;
using System.Text;
using System.Collections;
using MNPOS.Configuration;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace MNPOS.ViewModel
{
public class SignInViewModel : ViewModel
{
public SignInViewModel()
{
_appendDigitCommand = new Command<string>(AppendDigit);
_clearDigitCommand = new Command(ClearDigit);
_signInCommand = new Command(SignIn);
}

public void AppendDigit(string entry)
{
if (_pin.Count < Constants.MaximumPinLength)
{
_pin.Add(entry[0]);
}
}

public void ClearDigit()
{
if (_pin.Count > 0)
{
_pin.RemoveAt(Pin.Count - 1);
}
}

public void SignIn()
{

}

public Command AppendDigitCommand => _appendDigitCommand;
public Command ClearDigitCommand => _clearDigitCommand;
public Command SignInCommand => _signInCommand;
public ObservableCollection<char> Pin
{
get { return _pin; }
set
{
SetProperty<ObservableCollection<char>>(ref _pin, value, nameof(Pin));
}
}

private readonly Command _appendDigitCommand;
private readonly Command _clearDigitCommand;
private readonly Command _signInCommand;
private ObservableCollection<char> _pin = new ObservableCollection<char>();
}
}


ViewModel



using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MNPOS.ViewModel
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((propertyName)));
}

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);

return true;
}
}


}



BindableStackLayout



using System.Collections;
using Xamarin.Forms;

namespace MNPOS.View.Utility
{
public class BindableStackLayout : StackLayout
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());

public DataTemplate ItemDataTemplate
{
get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }
set { SetValue(ItemDataTemplateProperty, value); }
}
public static readonly BindableProperty ItemDataTemplateProperty =
BindableProperty.Create(nameof(ItemDataTemplate), typeof(DataTemplate), typeof(BindableStackLayout));

void PopulateItems()
{
if (ItemsSource == null) return;
foreach (var item in ItemsSource)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}
}
}









share|improve this question














I have a PIN field on a page. The PIN field is implemented with a custom class derived from stack layout, which adds item-source binding capabilities. Its item source is bound to an ObservableCollection of characters in my view-model. The issue I'm experiencing is as the title states, the pin field doesn't update upon adding, deleting from the ObservableCollection.



I've read posts with similar issues to mine. All of their solutions state to ensure that the ObservableCollection property notifies its property changed through the INotifyPropertyChanged interface call. I did this and it still isn't updating the GUI. Please help!



Here is the code:



The xaml for the PIN field



<utility:BindableStackLayout HeightRequest="40"
Orientation="Horizontal"
HorizontalOptions="Center"
ItemsSource="{Binding Pin}">
<utility:BindableStackLayout.ItemDataTemplate>
<DataTemplate>
<skia:SKCanvasView PaintSurface="OnPaintSurfacePinDigit"/>
</DataTemplate>
</utility:BindableStackLayout.ItemDataTemplate>
</utility:BindableStackLayout>


SignInPage.xaml.cs



using System;
using MNPOS.ViewModel;
using Xamarin.Forms;
using SkiaSharp.Views.Forms;
using SkiaSharp;

namespace MNPOS.View
{
public partial class SignInPage : CustomNavigationDetailPage
{
public SignInPage()
{
BindingContext = _viewModel;
InitializeComponent();
}

public void OnPaintSurfacePinDigit(object sender, SKPaintSurfaceEventArgs e)
{
...
}

private SignInViewModel _viewModel = new SignInViewModel();
}
}


SignInViewModel



using System;
using System.Text;
using System.Collections;
using MNPOS.Configuration;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace MNPOS.ViewModel
{
public class SignInViewModel : ViewModel
{
public SignInViewModel()
{
_appendDigitCommand = new Command<string>(AppendDigit);
_clearDigitCommand = new Command(ClearDigit);
_signInCommand = new Command(SignIn);
}

public void AppendDigit(string entry)
{
if (_pin.Count < Constants.MaximumPinLength)
{
_pin.Add(entry[0]);
}
}

public void ClearDigit()
{
if (_pin.Count > 0)
{
_pin.RemoveAt(Pin.Count - 1);
}
}

public void SignIn()
{

}

public Command AppendDigitCommand => _appendDigitCommand;
public Command ClearDigitCommand => _clearDigitCommand;
public Command SignInCommand => _signInCommand;
public ObservableCollection<char> Pin
{
get { return _pin; }
set
{
SetProperty<ObservableCollection<char>>(ref _pin, value, nameof(Pin));
}
}

private readonly Command _appendDigitCommand;
private readonly Command _clearDigitCommand;
private readonly Command _signInCommand;
private ObservableCollection<char> _pin = new ObservableCollection<char>();
}
}


ViewModel



using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace MNPOS.ViewModel
{
public abstract class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs((propertyName)));
}

protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value))
{
return false;
}
storage = value;
OnPropertyChanged(propertyName);

return true;
}
}


}



BindableStackLayout



using System.Collections;
using Xamarin.Forms;

namespace MNPOS.View.Utility
{
public class BindableStackLayout : StackLayout
{
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());

public DataTemplate ItemDataTemplate
{
get { return (DataTemplate)GetValue(ItemDataTemplateProperty); }
set { SetValue(ItemDataTemplateProperty, value); }
}
public static readonly BindableProperty ItemDataTemplateProperty =
BindableProperty.Create(nameof(ItemDataTemplate), typeof(DataTemplate), typeof(BindableStackLayout));

void PopulateItems()
{
if (ItemsSource == null) return;
foreach (var item in ItemsSource)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}
}
}






xamarin xamarin.forms binding observablecollection inotifypropertychanged






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Oct 28 '18 at 23:04









Cyrus HwangCyrus Hwang

1




1













  • an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

    – Jason
    Oct 29 '18 at 0:02



















  • an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

    – Jason
    Oct 29 '18 at 0:02

















an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

– Jason
Oct 29 '18 at 0:02





an ObservableCollection will fire a CollectionChanged event when items are added or removed. Your control needs to respond to that event

– Jason
Oct 29 '18 at 0:02












1 Answer
1






active

oldest

votes


















0














You will need to subscribe to Collection Change Events in your PropertyChanged event handler.



So in you BindableStackLayout Class change



public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());


to this:



public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems(oldValue, newValue));


Then change your PopulateItems to this:



void PopulateItems(IEnumerable oldValue, IEnumerable newValue)
{

if(oldItem != null)
((ObservableCollection<char>)oldItem).CollectionChanged -= CollectionChanged;

if (newValue == null)
{
Children.Clear();
return;
}

((ObservableCollection<char>)newItem).CollectionChanged -= CollectionChanged;


foreach (var item in newItem)
{
var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
itemTemplate.BindingContext = item;
Children.Add(itemTemplate);
}
}


Then the CollectionChanged method would look something like this:



        private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
int index = e.NewStartingIndex;
foreach (var item in e.NewItems)
Children.Insert(index++, GetItemView(item));
}
break;
case NotifyCollectionChangedAction.Move:
{
var item = ObservableSource[e.OldStartingIndex];
Children.RemoveAt(e.OldStartingIndex);
Children.Insert(e.NewStartingIndex, GetItemView(item));
}
break;
case NotifyCollectionChangedAction.Remove:
{
Children.RemoveAt(e.OldStartingIndex);
}
break;
case NotifyCollectionChangedAction.Replace:
{
Children.RemoveAt(e.OldStartingIndex);
Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
}
break;
case NotifyCollectionChangedAction.Reset:
Children.Clear();
foreach (var item in ItemsSource)
Children.Add(GetItemView(item));
break;
}
}


Please note that this was mostly typed in the browser, so there might be some typos but it should lead you in the right direction.



Good Luck






share|improve this answer
























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53036839%2fcustom-control-with-itemssource-bound-to-observablecollection-in-view-model-not%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    0














    You will need to subscribe to Collection Change Events in your PropertyChanged event handler.



    So in you BindableStackLayout Class change



    public static readonly BindableProperty ItemsSourceProperty =
    BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
    propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());


    to this:



    public static readonly BindableProperty ItemsSourceProperty =
    BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
    propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems(oldValue, newValue));


    Then change your PopulateItems to this:



    void PopulateItems(IEnumerable oldValue, IEnumerable newValue)
    {

    if(oldItem != null)
    ((ObservableCollection<char>)oldItem).CollectionChanged -= CollectionChanged;

    if (newValue == null)
    {
    Children.Clear();
    return;
    }

    ((ObservableCollection<char>)newItem).CollectionChanged -= CollectionChanged;


    foreach (var item in newItem)
    {
    var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
    itemTemplate.BindingContext = item;
    Children.Add(itemTemplate);
    }
    }


    Then the CollectionChanged method would look something like this:



            private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
    switch (e.Action)
    {
    case NotifyCollectionChangedAction.Add:
    {
    int index = e.NewStartingIndex;
    foreach (var item in e.NewItems)
    Children.Insert(index++, GetItemView(item));
    }
    break;
    case NotifyCollectionChangedAction.Move:
    {
    var item = ObservableSource[e.OldStartingIndex];
    Children.RemoveAt(e.OldStartingIndex);
    Children.Insert(e.NewStartingIndex, GetItemView(item));
    }
    break;
    case NotifyCollectionChangedAction.Remove:
    {
    Children.RemoveAt(e.OldStartingIndex);
    }
    break;
    case NotifyCollectionChangedAction.Replace:
    {
    Children.RemoveAt(e.OldStartingIndex);
    Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
    }
    break;
    case NotifyCollectionChangedAction.Reset:
    Children.Clear();
    foreach (var item in ItemsSource)
    Children.Add(GetItemView(item));
    break;
    }
    }


    Please note that this was mostly typed in the browser, so there might be some typos but it should lead you in the right direction.



    Good Luck






    share|improve this answer




























      0














      You will need to subscribe to Collection Change Events in your PropertyChanged event handler.



      So in you BindableStackLayout Class change



      public static readonly BindableProperty ItemsSourceProperty =
      BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
      propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());


      to this:



      public static readonly BindableProperty ItemsSourceProperty =
      BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
      propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems(oldValue, newValue));


      Then change your PopulateItems to this:



      void PopulateItems(IEnumerable oldValue, IEnumerable newValue)
      {

      if(oldItem != null)
      ((ObservableCollection<char>)oldItem).CollectionChanged -= CollectionChanged;

      if (newValue == null)
      {
      Children.Clear();
      return;
      }

      ((ObservableCollection<char>)newItem).CollectionChanged -= CollectionChanged;


      foreach (var item in newItem)
      {
      var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
      itemTemplate.BindingContext = item;
      Children.Add(itemTemplate);
      }
      }


      Then the CollectionChanged method would look something like this:



              private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
      {
      switch (e.Action)
      {
      case NotifyCollectionChangedAction.Add:
      {
      int index = e.NewStartingIndex;
      foreach (var item in e.NewItems)
      Children.Insert(index++, GetItemView(item));
      }
      break;
      case NotifyCollectionChangedAction.Move:
      {
      var item = ObservableSource[e.OldStartingIndex];
      Children.RemoveAt(e.OldStartingIndex);
      Children.Insert(e.NewStartingIndex, GetItemView(item));
      }
      break;
      case NotifyCollectionChangedAction.Remove:
      {
      Children.RemoveAt(e.OldStartingIndex);
      }
      break;
      case NotifyCollectionChangedAction.Replace:
      {
      Children.RemoveAt(e.OldStartingIndex);
      Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
      }
      break;
      case NotifyCollectionChangedAction.Reset:
      Children.Clear();
      foreach (var item in ItemsSource)
      Children.Add(GetItemView(item));
      break;
      }
      }


      Please note that this was mostly typed in the browser, so there might be some typos but it should lead you in the right direction.



      Good Luck






      share|improve this answer


























        0












        0








        0







        You will need to subscribe to Collection Change Events in your PropertyChanged event handler.



        So in you BindableStackLayout Class change



        public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
        propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());


        to this:



        public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
        propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems(oldValue, newValue));


        Then change your PopulateItems to this:



        void PopulateItems(IEnumerable oldValue, IEnumerable newValue)
        {

        if(oldItem != null)
        ((ObservableCollection<char>)oldItem).CollectionChanged -= CollectionChanged;

        if (newValue == null)
        {
        Children.Clear();
        return;
        }

        ((ObservableCollection<char>)newItem).CollectionChanged -= CollectionChanged;


        foreach (var item in newItem)
        {
        var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
        itemTemplate.BindingContext = item;
        Children.Add(itemTemplate);
        }
        }


        Then the CollectionChanged method would look something like this:



                private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        switch (e.Action)
        {
        case NotifyCollectionChangedAction.Add:
        {
        int index = e.NewStartingIndex;
        foreach (var item in e.NewItems)
        Children.Insert(index++, GetItemView(item));
        }
        break;
        case NotifyCollectionChangedAction.Move:
        {
        var item = ObservableSource[e.OldStartingIndex];
        Children.RemoveAt(e.OldStartingIndex);
        Children.Insert(e.NewStartingIndex, GetItemView(item));
        }
        break;
        case NotifyCollectionChangedAction.Remove:
        {
        Children.RemoveAt(e.OldStartingIndex);
        }
        break;
        case NotifyCollectionChangedAction.Replace:
        {
        Children.RemoveAt(e.OldStartingIndex);
        Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
        }
        break;
        case NotifyCollectionChangedAction.Reset:
        Children.Clear();
        foreach (var item in ItemsSource)
        Children.Add(GetItemView(item));
        break;
        }
        }


        Please note that this was mostly typed in the browser, so there might be some typos but it should lead you in the right direction.



        Good Luck






        share|improve this answer













        You will need to subscribe to Collection Change Events in your PropertyChanged event handler.



        So in you BindableStackLayout Class change



        public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
        propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems());


        to this:



        public static readonly BindableProperty ItemsSourceProperty =
        BindableProperty.Create(nameof(ItemsSource), typeof(IEnumerable), typeof(BindableStackLayout),
        propertyChanged: (bindable, oldValue, newValue) => ((BindableStackLayout)bindable).PopulateItems(oldValue, newValue));


        Then change your PopulateItems to this:



        void PopulateItems(IEnumerable oldValue, IEnumerable newValue)
        {

        if(oldItem != null)
        ((ObservableCollection<char>)oldItem).CollectionChanged -= CollectionChanged;

        if (newValue == null)
        {
        Children.Clear();
        return;
        }

        ((ObservableCollection<char>)newItem).CollectionChanged -= CollectionChanged;


        foreach (var item in newItem)
        {
        var itemTemplate = ItemDataTemplate.CreateContent() as Xamarin.Forms.View;
        itemTemplate.BindingContext = item;
        Children.Add(itemTemplate);
        }
        }


        Then the CollectionChanged method would look something like this:



                private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
        switch (e.Action)
        {
        case NotifyCollectionChangedAction.Add:
        {
        int index = e.NewStartingIndex;
        foreach (var item in e.NewItems)
        Children.Insert(index++, GetItemView(item));
        }
        break;
        case NotifyCollectionChangedAction.Move:
        {
        var item = ObservableSource[e.OldStartingIndex];
        Children.RemoveAt(e.OldStartingIndex);
        Children.Insert(e.NewStartingIndex, GetItemView(item));
        }
        break;
        case NotifyCollectionChangedAction.Remove:
        {
        Children.RemoveAt(e.OldStartingIndex);
        }
        break;
        case NotifyCollectionChangedAction.Replace:
        {
        Children.RemoveAt(e.OldStartingIndex);
        Children.Insert(e.NewStartingIndex, GetItemView(ObservableSource[e.NewStartingIndex]));
        }
        break;
        case NotifyCollectionChangedAction.Reset:
        Children.Clear();
        foreach (var item in ItemsSource)
        Children.Add(GetItemView(item));
        break;
        }
        }


        Please note that this was mostly typed in the browser, so there might be some typos but it should lead you in the right direction.



        Good Luck







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 16 '18 at 15:48









        Cornelius KrugerCornelius Kruger

        913




        913
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53036839%2fcustom-control-with-itemssource-bound-to-observablecollection-in-view-model-not%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Xamarin.iOS Cant Deploy on Iphone

            Glorious Revolution

            Dulmage-Mendelsohn matrix decomposition in Python